From 77633b188d01228383ced79c26b41ebb2989624c Mon Sep 17 00:00:00 2001
From: Normand Briere <nbriere@noware.ca>
Date: Sun, 07 Jul 2019 10:19:34 -0400
Subject: [PATCH] New 3ds loader.

---
 ObjEditor.java                                                         |   77 
 com/microcrowd/loader/java3d/max3ds/Main.java                          |  454 +++++
 com/microcrowd/loader/java3d/max3ds/chunks/FacesMaterialChunk.java     |   59 
 com/microcrowd/loader/java3d/max3ds/chunks/BooleanChunk.java           |   46 
 ObjectFile.java                                                        |   12 
 com/microcrowd/loader/java3d/max3ds/Loader3DS.java                     |  317 +++
 com/microcrowd/loader/java3d/max3ds/chunks/FloatChunk.java             |   47 
 com/microcrowd/loader/java3d/max3ds/chunks/FramesChunk.java            |   45 
 com/microcrowd/loader/java3d/max3ds/TextureImageLoader.java            |   42 
 com/microcrowd/loader/java3d/max3ds/chunks/AxisChunk.java              |   83 +
 com/microcrowd/loader/java3d/max3ds/chunks/Vertex2ListChunk.java       |   55 
 com/microcrowd/loader/java3d/max3ds/chunks/CameraChunk.java            |   65 
 com/microcrowd/loader/java3d/max3ds/chunks/Vertex3ListChunk.java       |   54 
 com/microcrowd/loader/java3d/max3ds/chunks/ScaleChunk.java             |   67 
 com/microcrowd/loader/java3d/max3ds/CannotChopException.java           |   40 
 CameraPane.java                                                        |   50 
 cRadio.java                                                            |    4 
 GroupEditor.java                                                       |   22 
 com/microcrowd/loader/java3d/max3ds/ChunkChopper.java                  |  635 +++++++
 com/microcrowd/loader/java3d/max3ds/DefaultTextureImageLoader.java     |  103 +
 com/microcrowd/loader/java3d/max3ds/ChunkMap.java                      |  444 +++++
 com/microcrowd/loader/java3d/max3ds/chunks/FacesDescriptionChunk.java  |  322 +++
 com/microcrowd/loader/java3d/max3ds/chunks/LightChunk.java             |   80 
 com/microcrowd/loader/java3d/max3ds/chunks/PositionChunk.java          |   63 
 com/microcrowd/loader/java3d/max3ds/chunks/SmoothingChunk.java         |   65 
 com/microcrowd/loader/java3d/max3ds/chunks/BoundingBoxChunk.java       |   55 
 com/microcrowd/loader/java3d/max3ds/chunks/ColorChunk.java             |   82 
 com/microcrowd/loader/java3d/max3ds/chunks/PercentageChunk.java        |   59 
 com/microcrowd/loader/java3d/max3ds/package.html                       |    3 
 com/microcrowd/loader/java3d/max3ds/chunks/FramesDescriptionChunk.java |   59 
 com/microcrowd/loader/java3d/max3ds/chunks/HierarchyInfoChunk.java     |   46 
 com/microcrowd/loader/java3d/max3ds/chunks/RotationChunk.java          |  129 +
 ClickInfo.java                                                         |    2 
 com/microcrowd/loader/java3d/max3ds/chunks/KeyFramerInfoChunk.java     |   68 
 com/microcrowd/loader/java3d/max3ds/chunks/SpotLightChunk.java         |   73 
 com/microcrowd/loader/java3d/max3ds/chunks/Chunk.java                  |  139 +
 com/microcrowd/loader/java3d/max3ds/chunks/MaterialChunk.java          |  121 +
 com/microcrowd/loader/java3d/max3ds/chunks/NamedObjectChunk.java       |   50 
 com/microcrowd/loader/java3d/max3ds/ChunkTester.java                   |   31 
 com/microcrowd/loader/java3d/max3ds/data/KeyFramer.java                |  526 ++++++
 com/microcrowd/loader/java3d/max3ds/chunks/StringChunk.java            |   52 
 com/microcrowd/loader/java3d/max3ds/chunks/PivotChunk.java             |   45 
 com/realvue/sim/ui/loader/java3d/max3ds/Loader3DS.java                 |   34 
 cFileSystemPane.java                                                   |   31 
 com/microcrowd/loader/java3d/max3ds/chunks/TextureChunk.java           |   47 
 com/microcrowd/loader/java3d/max3ds/chunks/GlobalColorChunk.java       |   45 
 Object3D.java                                                          |    4 
 47 files changed, 4,897 insertions(+), 55 deletions(-)

diff --git a/CameraPane.java b/CameraPane.java
index d0c68fd..9699e8e 100644
--- a/CameraPane.java
+++ b/CameraPane.java
@@ -14604,7 +14604,8 @@
         Globals.MOUSEDRAGGED = false;
         
         movingcamera = false;
-        X = Y = 0;
+        X = 0; // getBounds().width/2;
+        Y = 0; // getBounds().height/2;
         //System.out.println("mouseReleased: " + e);
         clickEnd(e.getX(), e.getY(), e.getModifiersEx());
     }
@@ -15717,23 +15718,42 @@
              */
             if (!isRenderer)
             {
-                object.drawEditHandles(info, 0);
-                
-                if (drag && (X != 0 || Y != 0) && object.selection.Size() > 0)
+                if (object.selection.Size() > 0)
                 {
-                    switch (object.selection.get(0).hitSomething)
+                    int hitSomething = object.selection.get(0).hitSomething;
+
+                    info.DX = 0;
+                    info.DY = 0;
+                    info.W = 1;
+                    if (hitSomething == Object3D.hitCenter)
                     {
-                        case Object3D.hitCenter: gr.setColor(Color.pink);
-                            gr.drawLine(X, Y, info.bounds.width/2, info.bounds.height/2);
-                            break;
-                        case Object3D.hitRotate: gr.setColor(Color.yellow);
-                            gr.drawLine(X, Y, info.bounds.width/2, info.bounds.height/2);
-                        break;
-                        case Object3D.hitScale: gr.setColor(Color.cyan);
-                            gr.drawLine(X, Y, info.bounds.width/2, info.bounds.height/2);
-                        break;
+                        info.DX = X;
+                        if (X != 0)
+                            info.DX -= info.bounds.width/2;
+
+                        info.DY = Y;
+                        if (Y != 0)
+                            info.DY -= info.bounds.height/2;
                     }
-                    
+
+                    object.drawEditHandles(info, 0);
+
+                    if (drag && (X != 0 || Y != 0))
+                    {
+                        switch (hitSomething)
+                        {
+                            case Object3D.hitCenter: gr.setColor(Color.pink);
+                                gr.drawLine(X, Y, info.bounds.width/2, info.bounds.height/2);
+                                break;
+                            case Object3D.hitRotate: gr.setColor(Color.yellow);
+                                gr.drawLine(X, Y, info.bounds.width/2, info.bounds.height/2);
+                            break;
+                            case Object3D.hitScale: gr.setColor(Color.cyan);
+                                gr.drawLine(X, Y, info.bounds.width/2, info.bounds.height/2);
+                            break;
+                        }
+
+                    }
                 }
             }
         }
diff --git a/ClickInfo.java b/ClickInfo.java
index a73de9f..0377695 100644
--- a/ClickInfo.java
+++ b/ClickInfo.java
@@ -26,6 +26,8 @@
     double toScreen[][];
     iCameraPane pane;
     Graphics g;
+    int DX, DY;
+    float W = 1;
 
     static double matbuffer[][] = new double[4][4];
 }
diff --git a/GroupEditor.java b/GroupEditor.java
index dab62a9..84ed69e 100644
--- a/GroupEditor.java
+++ b/GroupEditor.java
@@ -380,6 +380,7 @@
 		shadowYItem.addActionListener(this);
                 shadowZItem = menu.add(new MenuItem("Shadow Blue"));
 		shadowZItem.addActionListener(this);
+                
         if (Globals.ADVANCED)
         {
 		menu.add("-");
@@ -601,6 +602,10 @@
                 fullButton.setToolTipText("Full-screen window");
 		fullButton.addActionListener(this);
                 
+		oe.toolbarPanel.add(screenfitButton = GetButton("icons/fit.png", !Grafreed.NIMBUSLAF)); //, oe.aConstraints);
+                screenfitButton.setToolTipText("Screen fit");
+		screenfitButton.addActionListener(this);
+
 		oe.toolbarPanel.add(restoreCameraButton = GetButton("icons/eye.png", !Grafreed.NIMBUSLAF)); //, oe.aConstraints);
                 restoreCameraButton.setToolTipText("Restore viewpoint");
 		restoreCameraButton.addActionListener(this);
@@ -633,10 +638,6 @@
                                 
                 //oe.toolboxPanel.Return();
                 
-		copyOptionsPanel.add(screenfitButton = GetButton("icons/fit.png", !Grafreed.NIMBUSLAF)); //, oe.aConstraints);
-                screenfitButton.setToolTipText("Screen fit");
-		screenfitButton.addActionListener(this);
-
 //                copyOptionsPanel.add(trackCB = GetToggleButton("icons/track.png", CameraPane.TRACK)); //, oe.aConstraints);
 //                trackCB.setToolTipText("Enable tracking");
 //                                trackCB.addItemListener(this);
@@ -1214,8 +1215,6 @@
                         }
                     }
                     
-                    String string = (String) object;
-                    
                     System.out.println("Transfer = " + object + "; drop : " + target);
 //                    if( object instanceof java.io.File[])
 //                    {
@@ -1223,6 +1222,8 @@
 //                        objEditor.DropFile((java.io.File[]) object, true);
 //                        return;
 //                    }
+                    
+                    String string = (String) object;
                     
                     // File path for Mac and Windows
                     if (string.charAt(0) == '/' || string.charAt(1) == ':')
@@ -2219,7 +2220,8 @@
 		} else
 		if (source == undoButton)
 		{
-			Undo();
+			if (!Undo())
+                            java.awt.Toolkit.getDefaultToolkit().beep();
 		} else
 		if (source == redoButton)
 		{
@@ -2227,7 +2229,8 @@
 		} else
 		if (source == saveButton)
 		{
-			Save();
+			if (!Save(true))
+                            java.awt.Toolkit.getDefaultToolkit().beep();
 		} else
 		if (source == oneStepButton)
 		{
@@ -2236,17 +2239,14 @@
 		} else
 		if (source == screenfitButton)
 		{
-			//Reload(lastConverter, lastFilename, true);
                     ScreenFit();
 		} else
 		if (source == screenfitpointButton)
 		{
-			//Reload(lastConverter, lastFilename, true);
                     ScreenFitPoint();
 		} else
 		if (source == snapobjectButton)
 		{
-			//Reload(lastConverter, lastFilename, true);
                     SnapObject();
 		} else
 //		if (event.getSource() == recompileButton)
diff --git a/ObjEditor.java b/ObjEditor.java
index 93548e1..5d7d1bb 100644
--- a/ObjEditor.java
+++ b/ObjEditor.java
@@ -1979,8 +1979,9 @@
             // 3D models
             if (filename.endsWith(".3ds") || filename.endsWith(".3DS"))
             {
-                lastConverter = new com.jmex.model.converters.MaxToJme();
-                LoadFile(filename, lastConverter);
+                //lastConverter = new com.jmex.model.converters.MaxToJme();
+                //LoadFile(filename, lastConverter);
+                LoadObjFile(filename); // New 3ds loader
                 continue;
             }
             if (filename.endsWith(".dae") || filename.endsWith(".DAE"))
@@ -2706,6 +2707,7 @@
                 LA.matXRotate(((Object3D) group.get(group.size() - 1)).toParent, -Math.PI / 2);
                 LA.matXRotate(((Object3D) group.get(group.size() - 1)).fromParent, Math.PI / 2);
             }
+            
             //cJME.count++;
             //cJME.count %= 12;
             if (gc)
@@ -2889,6 +2891,7 @@
             }
         }
     }
+    
     cFileSystemPane FSPane;
 
     void SetMaterial(cMaterial mat, Object3D.cVector2[] others)
@@ -2942,6 +2945,7 @@
                 }
             }
         }
+        
         freezematerial = false;
     }
 
@@ -3566,6 +3570,28 @@
     
     public void Save()
     {
+        // Default reduces the probability of heuristics errors.
+        Save(true);
+    }
+    
+    private boolean Equal(byte[] compress, byte[] name)
+    {
+        if (compress.length != name.length)
+        {
+            return false;
+        }
+        
+        for (int i=compress.length; --i>=0;)
+        {
+            if (compress[i] != name[i])
+                return false;
+        }
+        
+        return true;
+    }
+
+    public boolean Save(boolean user)
+    {
         System.err.println("Save");
         
         cRadio tab = GetCurrentTab();
@@ -3576,18 +3602,31 @@
         copy.ExtractBigData(hashtable);
         
         byte[] compress = Compress(copy);
-        
-        //EditorFrame.m_MainFrame.requestFocusInWindow();
-        tab.graphs[tab.undoindex++] = compress;
-
-        copy.RestoreBigData(hashtable);
 
         CameraPane.SWITCH = temp;
         
+        boolean thesame = false;
+        
+        // Quick heuristic using length. Works only when stream is compressed.
+        if (tab.undoindex > 0 && tab.graphs[tab.undoindex-1] != null && Equal(compress, tab.graphs[tab.undoindex-1]))
+        {
+            thesame = true;
+        }
+        
+        //EditorFrame.m_MainFrame.requestFocusInWindow();
+        if (!thesame)
+        {
+            tab.user[tab.undoindex] = user;
+            tab.graphs[tab.undoindex++] = compress;
+        }
+
+        copy.RestoreBigData(hashtable);
+
         //assert(hashtable.isEmpty());
         
         for (int i = tab.undoindex; i < tab.graphs.length; i++)
         {
+            tab.user[i] = false;
             tab.graphs[i] = null;
         }
 
@@ -3611,6 +3650,8 @@
                 e.printStackTrace();
             }
         }
+        
+        return !thesame;
     }
 
     void CopyChanged(Object3D obj)
@@ -3667,7 +3708,7 @@
         redoButton.setEnabled(tab.graphs[tab.undoindex + 1] != null);
     }
     
-    public void Undo()
+    public boolean Undo()
     {
         System.err.println("Undo");
         
@@ -3676,18 +3717,27 @@
         if (tab.undoindex == 0)
         {
             java.awt.Toolkit.getDefaultToolkit().beep();
-            return;
+            return false;
         }
 
-        if (tab.graphs[tab.undoindex] == null)
+        if (tab.graphs[tab.undoindex] == null || !tab.user[tab.undoindex])
         {
-            Save();
-            tab.undoindex -= 1;
+            if (Save(false))
+                tab.undoindex -= 1;
+            else
+            {
+                if (tab.undoindex <= 0)
+                    return false;
+                else
+                    tab.undoindex -= 1;
+            }
         }
 
         tab.undoindex -= 1;
 
         CopyChanged((Object3D)Uncompress(tab.graphs[tab.undoindex]));
+        
+        return true;
     }
 
     public void Redo()
@@ -3703,6 +3753,9 @@
         tab.undoindex += 1;
 
         CopyChanged((Object3D)Uncompress(tab.graphs[tab.undoindex]));
+        
+        if (!tab.user[tab.undoindex])
+            tab.graphs[tab.undoindex] = null;
     }
 
         void ImportGFD()
diff --git a/Object3D.java b/Object3D.java
index 2a9ae20..442b170 100644
--- a/Object3D.java
+++ b/Object3D.java
@@ -7288,8 +7288,8 @@
 //            {
 //                CameraPane.Ymax = spoth;
 //            }
-            info.g.drawArc(boundary.x, boundary.y,
-                    boundary.width, boundary.height, 0, 360);
+            info.g.drawArc(boundary.x + info.DX, boundary.y + info.DY,
+                    (int)(boundary.width * info.W), (int)(boundary.height * info.W), 0, 360);
             //info.g.drawArc(spot.x, spotw, spot.width/2, boundary.height/2, 0, 360);
 //            if (CameraPane.Xmin > boundary.x)
 //            {
diff --git a/ObjectFile.java b/ObjectFile.java
index 5dad1c3..ceaa352 100644
--- a/ObjectFile.java
+++ b/ObjectFile.java
@@ -730,11 +730,15 @@
     public Scene load(String filename) throws FileNotFoundException,
             IncorrectFormatException, ParsingErrorException
     {
+        if (filename.toLowerCase().endsWith(".obj"))
+        {
+            setBasePathFromFilename(filename);
 
-        setBasePathFromFilename(filename);
-
-        Reader reader = new BufferedReader(new FileReader(filename));
-        return load(reader);
+            Reader reader = new BufferedReader(new FileReader(filename));
+            return load(reader);
+        }
+        else // new 3ds loader
+            return new com.microcrowd.loader.java3d.max3ds.Loader3DS().load(filename);
     } // End of load(String)
 
     private void setBaseUrlFromUrl(URL url)
diff --git a/cFileSystemPane.java b/cFileSystemPane.java
index 98b5413..05e14ef 100644
--- a/cFileSystemPane.java
+++ b/cFileSystemPane.java
@@ -18,6 +18,7 @@
     iCallBack owner;
     
     JButton refreshButton;
+    JButton rootButton;
     JButton loadButton;
     JButton printButton;
     JButton replaceButton;
@@ -39,7 +40,7 @@
         //ToolTipManager.sharedInstance().registerComponent(jTree);
         jTree.setCellRenderer(new cFileSystemModel.Renderer());
         
-        ResetModel();
+        ResetModel(true);
 
         JScrollPane tree = new JScrollPane(jTree);
         //jTree.addTreeSelectionListener(this);
@@ -64,11 +65,13 @@
         
         fileCommsnds.add(loadButton = new JButton("Load")); //, aConstraints);
                 loadButton.setToolTipText("Load selected file(s)");
-        fileCommsnds.add(refreshButton = new JButton("Refresh")); //, aConstraints);
-                refreshButton.setToolTipText("Refresh entire tree");
-        
-        refreshButton.addActionListener(this);
-        loadButton.addActionListener(this);
+                loadButton.addActionListener(this);
+        fileCommsnds.add(refreshButton = new JButton("User")); //, aConstraints);
+                refreshButton.setToolTipText("Refresh user tree");
+                refreshButton.addActionListener(this);
+        fileCommsnds.add(rootButton = new JButton("Root")); //, aConstraints);
+                rootButton.setToolTipText("Refresh root tree");
+                rootButton.addActionListener(this);
         
         if (Globals.ADVANCED)
         {
@@ -114,7 +117,7 @@
 //            /*DropTarget dropTarget =*/ new DropTarget(oe.cameraView, this);
     }
 
-    void ResetModel()
+    void ResetModel(boolean user)
     {
         cFilter filter = new cFilter();
         
@@ -134,9 +137,11 @@
         filter.add("tga");
         filter.add("bmp"); // not supported
         
+        File root = java.io.File.listRoots()[0];
+        File defaultDirectory = javax.swing.filechooser.FileSystemView.getFileSystemView().getDefaultDirectory();
+        
         jTree.setModel(new cFileSystemModel(
-                //java.io.File.listRoots()[1])
-                javax.swing.filechooser.FileSystemView.getFileSystemView().getDefaultDirectory(),
+                user?defaultDirectory:root,
                 filter
         ));
     }
@@ -167,7 +172,13 @@
     {
         if(event.getSource() == refreshButton)
         {
-            ResetModel();
+            ResetModel(true);
+            return;
+        }
+        
+        if(event.getSource() == rootButton)
+        {
+            ResetModel(false);
             return;
         }
         
diff --git a/cRadio.java b/cRadio.java
index 1ab12b3..03fab54 100644
--- a/cRadio.java
+++ b/cRadio.java
@@ -35,7 +35,9 @@
             camera = (Camera)Grafreed.clone(c);
     }
     
-    byte[] graphs[] = new byte[10000][];
+    byte[] graphs[] = new byte[100][];
+    boolean[] user = new boolean[100];
+    
     int undoindex = 0;
     
     // Patch to avoid bug with transparency.
diff --git a/com/microcrowd/loader/java3d/max3ds/CannotChopException.java b/com/microcrowd/loader/java3d/max3ds/CannotChopException.java
new file mode 100644
index 0000000..4985364
--- /dev/null
+++ b/com/microcrowd/loader/java3d/max3ds/CannotChopException.java
@@ -0,0 +1,40 @@
+/**
+ * Make a donation http://sourceforge.net/donate/index.php?group_id=98797
+ * Microcrowd.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * Contact Josh DeFord jdeford@microcrowd.com
+ */
+package com.microcrowd.loader.java3d.max3ds;
+
+/**
+ * Exception thrown by chunks to indicate that the
+ * chopper cannot appropriately chop(parse) it.
+ * This Exception won't return null for getCause()
+ */
+public class CannotChopException extends Exception
+{
+    public CannotChopException(Throwable cause)
+    {
+        this("", cause);
+    }
+
+    public CannotChopException(String message, Throwable cause)
+    {
+        super(message, cause);
+    }
+
+}
diff --git a/com/microcrowd/loader/java3d/max3ds/ChunkChopper.java b/com/microcrowd/loader/java3d/max3ds/ChunkChopper.java
new file mode 100644
index 0000000..83b91e1
--- /dev/null
+++ b/com/microcrowd/loader/java3d/max3ds/ChunkChopper.java
@@ -0,0 +1,635 @@
+/**
+ * Make a donation http://sourceforge.net/donate/index.php?group_id=98797
+ * Microcrowd.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * Contact Josh DeFord jdeford@realvue.com
+ */
+
+package com.microcrowd.loader.java3d.max3ds;
+
+import java.awt.Image;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.channels.Channels;
+import java.nio.channels.ReadableByteChannel;
+import java.util.HashMap;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import javax.media.j3d.Behavior;
+import javax.media.j3d.BranchGroup;
+import javax.media.j3d.Light;
+import javax.media.j3d.Texture;
+import javax.media.j3d.TransformGroup;
+import javax.vecmath.Point3f;
+import javax.vecmath.Vector3f;
+import com.microcrowd.loader.java3d.max3ds.chunks.Chunk;
+import com.microcrowd.loader.java3d.max3ds.data.KeyFramer;
+import com.sun.j3d.loaders.SceneBase;
+import com.sun.j3d.utils.image.TextureLoader;
+
+/**
+ * A singleton flyweight factory responsible for chopping the 
+ * data up and sending it to the corresponding 
+ * chunks(which are flyweights ala the flyweight pattern)
+ * for processing.
+ * This will sequentially read a 3ds file, load or
+ * skip chunks and subchunks and initialize the data
+ * for the chunks.  
+ * <p>
+ * Retrieved data may be stored as state in the ChunkChopper
+ * via {@link #pushData} for use by other chunks.
+ * <p> 
+ * Features not supported; unknown chunks are skipped.
+ */
+public class ChunkChopper
+{
+    private Logger logger = Logger.getLogger(ChunkChopper.class.getName());
+
+    private Loader3DS      loader;
+    private BranchGroup    sceneGroup;
+    private SceneBase      base;
+    private HashMap        dataMap;
+    private ByteBuffer     chunkBuffer;
+    private Integer        chunkID;
+
+    private TransformGroup currentGroup;
+    private String         currentObjectName;
+    private ChunkTester chunkTester = new ChunkTester();
+    private Chunk mainChunk = new Chunk("MainChunk");
+    private ChunkMap chunkMap = new ChunkMap(mainChunk);
+
+    private KeyFramer keyFramer = new KeyFramer();
+
+    /** This should be turned on by Loader3DS to view debugging information. */
+    public static boolean debug;
+
+    /** Current chunk for which debugging info is viewed if debug == true */
+    public static Chunk debugChunk;
+
+    /**
+     * private singleton constructor.
+     */
+    public ChunkChopper(){}
+
+    /**
+     * This sequentially parses the chunks out of the input stream and
+     * constructs the 3D entities represented within.
+     * A Chunk is a little endian data structure consists of a 
+     * 6 byte header followed by subchunks and or data.  
+     * The first short int(little endian) represent the id 
+     * of the chunk.  The next int represent the total 
+     * length of the chunk(total of data, subchunks and chunk header).
+     * <p> 
+     * The first chunk is the main chunk (id=4D4D) and its length
+     * is always the length of the file. It only contains sub chunks.
+     * Other chunks may contain data, subchunks or both.  If the format
+     * of a chunk is unknown skipped.
+     * <p>
+     * Subclasses of chunk will all automagically load the subchunks.  
+     * It is the programmers responsibility to ensure that the data 
+     * preceeding the subchunks is loaded or skipped as 
+     * required and that something useful is done with the data.  If data from the
+     * subchunks is needed in order to initialize components then that code should be
+     * placed in {@link Chunk#initialize}.  Otherwise the data may be dealt with in
+     * {@link Chunk#loadData}.  Also, if a chunk has data preceeding its subchunks
+     * it communicates how many bytes long that data is by returning it from loadData.
+     * <p>
+     * This chopper reads a file in order from beginning to end
+     * @param inputStream the stream with the data to be parsed.
+     * @param loader the loader that will be configured from the data.
+     * @param modelName name of the model file for display purposes.
+     * @param modelSize size in bytes of the file to read.
+     */
+    public synchronized SceneBase loadSceneBase(InputStream inputStream, Loader3DS loader, int modelSize)
+    {
+        this.loader = loader;
+        this.sceneGroup = new BranchGroup();
+        this.base = new SceneBase();
+        this.dataMap = new HashMap();
+        base.setSceneGroup(sceneGroup);
+
+        //FileChannel channel = null; 
+        ReadableByteChannel channel = null; 
+        try {
+            channel = Channels.newChannel(inputStream);
+            chunkBuffer = getByteBuffer(channel, modelSize);
+            //chunkBuffer = getDirectByteBuffer(channel, modelSize);
+
+            int mainChunkID     = chunkBuffer.getShort();
+            long mainChunkLength = chunkBuffer.getInt();
+
+            long begin = System.currentTimeMillis();     
+            logger.finest("\n\n\n STARTING SUBCUNKS " + (mainChunkLength - 61));
+            try {
+                loadSubChunks(mainChunk, 0);
+            }
+            catch(CannotChopException e){
+                
+            }
+            logger.finest("FINISHED WITH THE SUBCHUNKS");
+        }
+        catch (Exception e) {
+            e.printStackTrace();
+        }
+        finally
+        {
+            try {
+                if(channel != null) {
+                    channel.close();
+                }
+            } catch (Exception e){
+                //Just closing file.. don't care.
+            }
+        }
+        return base;
+    }
+
+    /**
+     * Allocates and loads a byte buffer from the channel
+     * @param channel the file channel to load the data from
+     * @return a direct byte buffer containing all the data of the channel at position 0 
+     */
+    public ByteBuffer getByteBuffer(ReadableByteChannel channel, int channelSize) throws IOException
+    {
+        ByteBuffer chunkBuffer = ByteBuffer.allocate(channelSize);
+        chunkBuffer.order(ByteOrder.LITTLE_ENDIAN);
+        channel.read(chunkBuffer);
+        chunkBuffer.position(0);
+        return chunkBuffer;
+    }
+
+
+    /**
+     * The base class Chunk takes care of loading subchunks for
+     * all chunks types.  It occurs as follows:
+     * <ol>
+     *     <li>The chunk id (short) is read
+     *     <li>The chunk length(int) is read
+     *     <li>A subchunk is looked up from the map of publish 
+     *         subchunk types of the current chunk.
+     *     <li>If it isn't found during the lookup it is skipped.
+     *     <li>Otherwise it is requested to {@link #pushData} 
+     *     <li>The return value, if there is one, is used to determine 
+     *         where its next subchunk is. A return value of 0 signifies
+     *         that the next subchunk is nigh.
+     *     <li>The chunk's subchunks are then loaded.
+     *     <li>The chunks initialize method is called.
+     * </ol>
+     */
+    protected void loadSubChunks(Chunk parentChunk, int level) throws CannotChopException
+    {
+        level++;
+        while(chunkBuffer.hasRemaining())//hasRemaining() evaluates limit - position.
+        {
+            chunkID         = new Integer(chunkBuffer.getShort());
+            Chunk chunk     = parentChunk.getSubChunk(chunkID);
+
+            int currentChunkLength     = chunkBuffer.getInt() - 6;  //length includes this 6 byte header.
+            int finishedPosition       = chunkBuffer.position() + currentChunkLength;
+            int previousLimit          = chunkBuffer.limit();
+            chunkBuffer.limit(chunkBuffer.position() + currentChunkLength);
+
+            if(debug) {
+                debug(parentChunk, level, chunkID, currentChunkLength, chunkBuffer.position(), chunkBuffer.limit());
+            }
+            if(chunk != null && currentChunkLength != 0) {
+                try {
+                    chunk.loadData(this);
+                }
+                catch(BufferUnderflowException e){
+                    chunkBuffer.position(finishedPosition);
+                    chunkBuffer.limit(previousLimit);
+                    throw new CannotChopException(" tried to read too much data from the buffer. Trying to recover.", e);
+                }
+                try {
+                    if(chunkBuffer.hasRemaining()) {
+                        loadSubChunks(chunk, level);
+                    }
+                    chunk.initialize(this);
+                }
+                catch(CannotChopException e){
+                    logger.log(Level.SEVERE, chunk.toString() + "Trying to continue");
+                }
+            } 
+
+            chunkBuffer.position(finishedPosition);
+            chunkBuffer.limit(previousLimit);
+        }
+    }
+
+    /**
+     * Gets the key framer chunk
+     * These should be their own objects instead of chunks.
+     */
+    public KeyFramer getKeyFramer()
+    {
+        return keyFramer;
+    }
+
+    /**
+     * Adds a group to the choppers scene group
+     * and sets the current name and group.
+     * @param the name of the object to add which
+     * will also be the current name of the object
+     * the chopper is working with.
+     * @param group the current group that the chopper
+     * will be adding things too.
+     */
+    public void addObject(String name, TransformGroup group)
+    {
+        sceneGroup.addChild(group);
+        currentGroup = group;
+        currentObjectName = name;
+        base.addNamedObject(name, group);
+    }
+
+
+
+    /**
+     * Gets the name of the current object
+     * the chopper is working with.  The value returned
+     * from this generally is either set by a NamedObjectChunk 
+     * and is the name of the name of the last object added
+     * or is the name set by setObjectName.
+     * @return the name of the current object being 
+     * constructed.
+     */
+    public String getObjectName()
+    {
+        return currentObjectName;
+    }
+
+
+    /**
+     * Sets the name of the current object.
+     * The name of the current object can also be set
+     * with {@link #addObject}
+     * @param name the name that the current object should be set to.
+     */
+    public void setObjectName(String name)
+    {
+        currentObjectName = name;
+    }
+
+    /**
+     * Gets the group for the current object 
+     * the chopper is working with.  The value returned
+     * from this generally gets set by a NamedObjectChunk 
+     * and is the name of the last object added.
+     * @return the group for the current object being 
+     * constructed.
+     */
+    public TransformGroup getGroup()
+    {
+        return currentGroup;
+    }
+
+    /**
+     * Used to store data that may later be used
+     * by another chunk.
+     * @param key the look up key.
+     * @param data the data to store.
+     */
+    public void pushData(Object key, Object data)
+    {
+        dataMap.put(key, data);
+    }
+
+    /**
+     * Gets a datum that had been retrieved and stored
+     * via {@link #pushData} earlier and removes it.
+     * @param key the key used to store the datum earlier.
+     */
+    public Object popData(Object key)
+    {
+        Object retVal = dataMap.remove(key);
+        return retVal;
+    }
+
+    /**
+     * Sets a named object in the loader.
+     * @param key the key name of the object
+     * @param value the named Object.
+     */
+    public void setNamedObject(String key, Object value)
+    {
+        base.addNamedObject(key, value);
+    }
+
+    /**
+     * Returns true if there have been lights loaded.
+     * @return true if there are lights.
+     */
+    public boolean hasLights()
+    {
+        return (base.getLightNodes() != null && base.getLightNodes().length > 0);
+    }
+
+    /**
+     * Adds a behavior to the scene base.
+     * @param behavior the behavior to add to the scene base.
+     */
+    public void addBehaviorNode(Behavior behavior)
+    {
+        base.addBehaviorNode(behavior);
+    }
+
+
+    /**
+     * Adds a light to the scene base.
+     * @param light the light to add to the scene base.
+     */
+    public void addLightNode(Light light)
+    {
+        base.addLightNode(light);
+    }
+
+
+    /**
+     * Adds a camera transform to the scene base.
+     * @param viewGroup the transform group to add as a view.
+     */
+    public void addViewGroup(TransformGroup viewGroup)
+    {
+        base.addViewGroup(viewGroup);
+    }
+
+    /**
+     * Sets a named Object in the loader.
+     * @param key the key used as the name for which the object will be returned
+     */
+    public Object getNamedObject(String key)
+    {
+        if(key == null)
+            return null;
+        return base.getNamedObjects().get(key);
+    }
+
+    /**
+     * Gets and cast the named object for the
+     * key provided.  Its an error if its not
+     * a transform group.
+     */
+    public TransformGroup getNamedTransformGroup(String key)
+    {
+        Object object = getNamedObject(key);
+        if(object instanceof TransformGroup)
+        {
+            return (TransformGroup)object;
+        }
+        else if (object != null)
+        {
+            logger.log(Level.INFO, "Retrieving " + key + " which is a named object but not useable because "+
+                               " its not a transform group. Its a " + object.getClass().getName());
+        }
+        return null;
+    }
+
+
+    /**
+     * Gets a long from the chunk Buffer
+     */
+    public long getLong()
+    {
+        return chunkBuffer.getLong();
+    }
+
+    /**
+     * Reads a short and returns it as a signed
+     * int.
+     */
+    public int getShort()
+    {
+        return chunkBuffer.getShort();
+    }
+
+    /**
+     * Reads a short and returns it as an unsigned
+     * int.
+     */
+    public int getUnsignedShort()
+    {
+        return chunkBuffer.getShort()&0xFFFF;
+    }
+
+    /**
+     * reads a float from the chunkBuffer.
+     */
+    public float getFloat()
+    {
+        return chunkBuffer.getFloat();
+    }
+
+    /**
+     * Reads 3 floats x,z,y from the chunkbuffer.
+     * Since 3ds has z as up and y as pointing in whereas
+     * java3d has z as pointing forward and y as pointing up;
+     * this returns new Vector3f(x,-z,y)
+     *
+     */
+    public Vector3f getVector()
+    {
+        return new Vector3f(getPoint());
+    }
+    /**
+     * Reads 3 floats x,z,y from the chunkbuffer.
+     * Since 3ds has z as up and y as pointing in whereas
+     * java3d has z as pointing forward and y as pointing up;
+     * this returns new Point3f(x,-z,y)
+     */
+    public Point3f getPoint()
+    {
+        float x = chunkBuffer.getFloat();
+        float z = -chunkBuffer.getFloat();
+        float y = chunkBuffer.getFloat();
+        return new Point3f(x,y,z);
+    }
+
+    /**
+     * Reads an int and returns it 
+     * @return the int read
+     */
+    public int getInt()
+    {
+        return chunkBuffer.getInt();
+    }
+
+    /**
+     * Reads an int and returns it 
+     * unsigned, any ints greater than MAX_INT
+     * will break.
+     */
+    public int getUnsignedInt()
+    {
+        return chunkBuffer.getInt()&0xFFFFFFFF;
+    }
+
+    /**
+     * Reads a byte, unsigns it, returns the corresponding int.
+     * @return the unsigned int corresponding to the read byte.
+     */
+    public int getUnsignedByte()
+    {
+        return chunkBuffer.get()&0xFF;
+    }
+
+    /**
+     * Reads a number of bytes corresponding to the
+     * number of bytes left in the current chunk and returns an array
+     * containing them.
+     * @return an array containing all the bytes for the current chunk. 
+     */
+    public byte[] getChunkBytes()
+    {
+        byte[] retVal = new byte[chunkBuffer.limit() - chunkBuffer.position()];
+        get(retVal);
+        return retVal;
+    }
+
+    /**
+     * Fills bytes with data from the chunk buffer.
+     * @param bytes the array to fill with data.
+     */
+    public void get(byte[] bytes)
+    {
+        chunkBuffer.get(bytes);
+    }
+
+
+    /**
+     * Sets the data map used to store values
+     * that chunks may need to retrieve later.
+     * @param dataMap the hashmap that will be used to store
+     * and retrieve values for use by chunks.
+     */
+    public void setDataMap(HashMap dataMap)
+    {
+        this.dataMap = dataMap;
+    }
+
+
+    /**
+     * This reads bytes until it gets 0x00 and returns
+     * the corresponding string.
+     */
+    public String getString()
+    {
+        StringBuffer stringBuffer = new StringBuffer();
+        char charIn = (char)chunkBuffer.get();
+        while(charIn != 0x00) 
+        {
+            stringBuffer.append(charIn);
+            charIn = (char)chunkBuffer.get();
+        }
+        return stringBuffer.toString();
+    }
+
+    /**
+     * Gets the id of the current chunk.
+     * @return id of the current chunk as read
+     * from the chunkBuffer.  It will be a signed <code>short</code>.
+     */
+    public Integer getID()
+    {
+        return chunkID;
+    }
+
+    /**
+     * Loads the image to server as a texture.
+     * @param textureImageName name of the image that 
+     * is going to be set to be the texture.
+     */
+    public Texture createTexture(String textureImageName)
+    {
+        Image image = loader.getTextureImage(textureImageName);
+        if(image == null)
+        {
+            System.err.println("Cannot load texture image " + textureImageName +
+                               ". Make sure it is in the directory with the model file. " +
+                               "If its a bmp make sure JAI is installed.");
+            return null;
+        }
+        try 
+        {
+            TextureLoader textureLoader = new TextureLoader(image, null);
+            return textureLoader.getTexture(); 
+        }
+        catch(Exception e){
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * prints some handy information... the chunk hierarchy.
+     */
+    protected void debug(Chunk parentChunk, int level, Integer chunkID, long chunkLength, int position, long limit)
+    {
+        try {
+       for(int i=0; i<level; i++)
+       {
+           System.out.print("  ");
+       }
+       Object child = parentChunk.getSubChunk(chunkID);
+       int id = ((short)chunkID.intValue()) & 0xFFFF;
+       System.out.println(parentChunk + " is " +
+                         (child==null?"skipping":"LOADING")+
+                         ": [id=" + Integer.toHexString(id) + 
+                         ", object= <" + parentChunk.getSubChunk(chunkID) +
+                         ">, chunkLength=" + chunkLength + 
+                         ", position=" + position + 
+                         " limit=" + limit + "]");
+        }
+        catch(Exception e){
+            //We're debugging.. its ok
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * Prints an exception and exits.
+     */
+    private void exceptAndExit(Throwable exception)
+    {
+        logger.log(Level.SEVERE, "\nThe chunk for loadData method read too much or not enough data from the stream." +
+                            " It needs be skipped or adjusted to read more or less data.");
+        exception.printStackTrace();
+        System.exit(3);
+    }
+
+    /**
+     * Convert the integer to an unsigned number.
+     * @param i the integer to convert.
+     */
+    private static String byteString(int i) 
+    {
+        final char[] digits = {
+	    '0' , '1' , '2' , '3' , '4' , '5' ,
+	    '6' , '7' , '8' , '9' , 'a' , 'b' ,
+	    'c' , 'd' , 'e' , 'f' };
+
+        char[] buf = new char[2];
+        buf[1] = digits[i & 0xF];
+        i >>>= 4;
+        buf[0] = digits[i & 0xF];
+
+        return "0x" + new String(buf).toUpperCase();
+    }
+}
diff --git a/com/microcrowd/loader/java3d/max3ds/ChunkMap.java b/com/microcrowd/loader/java3d/max3ds/ChunkMap.java
new file mode 100644
index 0000000..63a777d
--- /dev/null
+++ b/com/microcrowd/loader/java3d/max3ds/ChunkMap.java
@@ -0,0 +1,444 @@
+/**
+ * Make a donation http://sourceforge.net/donate/index.php?group_id=98797
+ * Microcrowd.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * Contact Josh DeFord jdeford@microcrowd.com
+ */
+
+package com.microcrowd.loader.java3d.max3ds;
+
+import java.util.HashMap;
+import com.microcrowd.loader.java3d.max3ds.chunks.AxisChunk;
+import com.microcrowd.loader.java3d.max3ds.chunks.BooleanChunk;
+import com.microcrowd.loader.java3d.max3ds.chunks.BoundingBoxChunk;
+import com.microcrowd.loader.java3d.max3ds.chunks.CameraChunk;
+import com.microcrowd.loader.java3d.max3ds.chunks.Chunk;
+import com.microcrowd.loader.java3d.max3ds.chunks.ColorChunk;
+import com.microcrowd.loader.java3d.max3ds.chunks.FacesDescriptionChunk;
+import com.microcrowd.loader.java3d.max3ds.chunks.FacesMaterialChunk;
+import com.microcrowd.loader.java3d.max3ds.chunks.FloatChunk;
+import com.microcrowd.loader.java3d.max3ds.chunks.FramesChunk;
+import com.microcrowd.loader.java3d.max3ds.chunks.FramesDescriptionChunk;
+import com.microcrowd.loader.java3d.max3ds.chunks.GlobalColorChunk;
+import com.microcrowd.loader.java3d.max3ds.chunks.HierarchyInfoChunk;
+import com.microcrowd.loader.java3d.max3ds.chunks.KeyFramerInfoChunk;
+import com.microcrowd.loader.java3d.max3ds.chunks.LightChunk;
+import com.microcrowd.loader.java3d.max3ds.chunks.MaterialChunk;
+import com.microcrowd.loader.java3d.max3ds.chunks.NamedObjectChunk;
+import com.microcrowd.loader.java3d.max3ds.chunks.PercentageChunk;
+import com.microcrowd.loader.java3d.max3ds.chunks.PivotChunk;
+import com.microcrowd.loader.java3d.max3ds.chunks.PositionChunk;
+import com.microcrowd.loader.java3d.max3ds.chunks.RotationChunk;
+import com.microcrowd.loader.java3d.max3ds.chunks.ScaleChunk;
+import com.microcrowd.loader.java3d.max3ds.chunks.SmoothingChunk;
+import com.microcrowd.loader.java3d.max3ds.chunks.SpotLightChunk;
+import com.microcrowd.loader.java3d.max3ds.chunks.StringChunk;
+import com.microcrowd.loader.java3d.max3ds.chunks.TextureChunk;
+import com.microcrowd.loader.java3d.max3ds.chunks.Vertex2ListChunk;
+import com.microcrowd.loader.java3d.max3ds.chunks.Vertex3ListChunk;
+
+
+/**
+ * A Hashmap with the chunk names as values with keys
+ * being the chunk id.
+ */
+public class ChunkMap extends HashMap
+{
+    private Chunk mainChunk;
+
+    /** Constant designating a chunk as a frames chunk*/
+    public static final Integer FRAMES_CHUNK = new Integer((short)0x0B008);
+    /** Constant designating a chunk as a mesh info chunk*/
+    //public static final Integer AMBIENT_LIGHT_INFO = new Integer((short)0x0B001);
+    public static final Integer MESH_INFO = new Integer((short)0x0B002);
+    //public static final Integer CAMERA_INFO = new Integer((short)0x0B003);
+    //public static final Integer CAMERA_TARGET_INFO = new Integer((short)0x0B004);
+    //public static final Integer OMNI_LIGHT_INFO = new Integer((short)0x0B005);
+    //public static final Integer SPOT_LIGHT_TARGET_INFO = new Integer((short)0x0B006);
+    public static final Integer SPOT_LIGHT_INFO = new Integer((short)0x0B007);
+    /** Key for the name and flags chunk */
+    public static final Integer NAME_AND_FLAGS = new Integer((short)0xB010);
+    /** Key for the pivot chunk */
+    public static final Integer PIVOT = new Integer((short)0xB013);
+    /** Indicates a position track chunk **/
+    public static final Integer POSITION = new Integer((short)0xB020);
+    /** Indicates a scale track chunk */
+    public static final Integer SCALE_TRACK= new Integer((short)0xB022);
+    /** Indicates a rotation track chunk */
+    public static final Integer ROTATION= new Integer((short)0xB021);
+    public static final Integer BOUNDING_BOX    = new Integer((short)0x0B014);
+    /** Indicates a hierarchy info chunk **/
+    public static final Integer HIERARCHY_INFO= new Integer((short)0xB030);
+    /** Signifies that the light is off **/
+    //public static final Integer LIGHT_OFF = new Integer((short)0x4620);
+    /** Signifies that the light is attenuated **/
+    public static final Integer ATTENUATED = new Integer((short)0x4625);
+    public static final Integer RANGE_START = new Integer((short)0x4659);
+    public static final Integer RANGE_END = new Integer((short)0x465A);
+    public static final Integer MULTIPLIER = new Integer((short)0x465B);
+    public static final Integer SPOTLIGHT = new Integer((short)0x4610);
+    public static final Integer COLOR     = new Integer((short)0x0010);
+    public static final Integer VERSION = new Integer((short)0x2);
+    public static final Integer EDITOR = new Integer((short)0x3D3D);
+    public static final Integer KEYFRAMER = new Integer((short)0xB000);
+    /** These are the chunk ids for colors */
+    public static final Integer MATERIAL_NAME = new Integer((short)0xA000);
+    /** ID of the chunk that will be used to represent the ambient color. **/
+    public static final Integer AMBIENT_COLOR = new Integer((short)0xA010);
+    /** ID of the chunk that will be used to represent the diffuse color. **/
+    public static final Integer DIFFUSE_COLOR = new Integer((short)0xA020);
+    /** ID of the chunk that will be used to represent the specular color. **/
+    public static final Integer SPECULAR_COLOR = new Integer((short)0xA030);
+    /** ID of the chunk that will be used to represent the shinines. **/
+    public static final Integer SHININESS = new Integer((short)0xA040);
+    //public static final Integer SHININESS = new Integer((short)0xA041);
+    /** ID of the chunk that will be used to represent the transparency. **/
+    public static final Integer TRANSPARENCY = new Integer((short)0xA050);
+    /** ID of the chunk that will be used to represent the two sided. **/
+    public static final Integer TWO_SIDED = new Integer((short)0xA081);
+    /** ID of the chunk that will be used to represent the texture. **/
+    public static final Integer TEXTURE = new Integer((short)0xA200);
+    /** ID of the chunk that will be used to represent the self illumination. **/
+    /** Represent a mesh object for shapes. */
+    public static final Integer MESH = new Integer((short)0x4100);
+    /** Represent a camera for viewing */
+    public static final Integer CAMERA = new Integer((short)0x4700);
+    /** Represent a light */
+    public static final Integer LIGHT = new Integer((short)0x4600);
+    /** Signifies that the light is off **/
+    //public static final Integer LIGHT_OFF      = new Integer((short)0x4620);
+    //public static final Integer RAYTRACE       = new Integer((short)0x4627);
+    //public static final Integer SHADOWED       = new Integer((short)0x4630);
+    //public static final Integer SHADOW_MAP     = new Integer((short)0x4641);
+    //public static final Integer SHOW_CONE      = new Integer((short)0x4650);
+    //public static final Integer RECTANGULAR    = new Integer((short)0x4651);
+    //public static final Integer OVERSHOOT      = new Integer((short)0x4652);
+    //public static final Integer SPOT_MAP       = new Integer((short)0x4653);
+    //public static final Integer SPOT_ROLL      = new Integer((short)0x4656);
+    //public static final Integer RAY_TRACE_BIAS = new Integer((short)0x4658);
+    /** the id of a texture name chunk.*/
+    public static final Integer TEXTURE_NAME = new Integer((short)0xA300);
+    public static final int TEXTURE_TILING = 0xA351;
+    public static final int TEXBLUR = 0xA353;
+    /** The vertex list from which vertices of a face array will be used. */
+    public static final Integer VERTEX_LIST = new Integer((short)0x4110);
+    /** reference coordinates into the vertex list which represent texture coordinates. */
+    public static final Integer TEXTURE_COORDINATES = new Integer((short)0x4140);
+    /** Local coordinate system of the mesh. */
+    public static final Integer COORDINATE_AXES = new Integer((short)0x4160);
+    /** reference coordinates into the vertex list which represent shape vertex coordinates. */
+    public static final Integer FACES_DESCRIPTION = new Integer((short)0x4120);
+    public static final Integer MATERIAL = new Integer((short)0xAFFF);
+    public static final Integer SCALE = new Integer((short)0x100);
+    public static final Integer NAMED_OBJECT = new Integer((short)0x4000);
+    /**  Key mapping faces material chunk as a child of this chunk */
+    public static final Integer FACES_MATERIAL = new Integer((short)0x4130);
+    /**  Key mapping smoothing chunk as a child of this chunk */
+    public static final Integer SMOOTH = new Integer((short)0x4150);
+
+    /**
+     * singleton constructor.
+     */
+    public ChunkMap(Chunk mainChunk)
+    {
+        this.mainChunk = mainChunk;
+        initializeDataMap();
+    }
+
+    public Chunk get(Integer chunkID)
+    {
+        return (Chunk)super.get(chunkID);
+    }
+
+    /**
+     * looks up the chunk corresponding to chunkID
+     * in the chopper's cache. If its not there
+     * look it up from the parent chunk provided.
+     * @param chunkID the id of the chunk to lookup
+     * @return the chunk for chunkID
+    public synchronized Chunk getChunk(Chunk parentChunk, Integer chunkID)
+    {
+        Chunk chunk = (Chunk)get(chunkID);
+        if(chunk == null && parentChunk != null)
+        {
+            chunk = (Chunk)(parentChunk.getChunkMap().get(chunkID)); //look up chunk from its parent.
+            put(chunkID, chunk);
+        }
+        return chunk;
+    }
+     */
+
+    /**
+     * Called when debugging is turned on. The keys are cast to short so that
+     * they are improperly signed since java will be reading improperly signed
+     * ids out of the file.
+     */
+    private void initializeDataMap()
+    {
+        Chunk keyFramerChunk         = new Chunk("KeyFramerChunk");
+        Chunk editorChunk            = new Chunk("EditorChunk");
+        Chunk triangularMeshChunk    = new Chunk("TriangularMeshChunk");
+
+        Chunk facesDescriptionChunk  = new FacesDescriptionChunk();
+        Chunk framesDescriptionChunk = new FramesDescriptionChunk();
+        Chunk textureChunk           = new TextureChunk();
+        Chunk lightChunk             = new LightChunk();
+        Chunk namedObjectChunk       = new NamedObjectChunk();
+        Chunk materialChunk          = new MaterialChunk();
+        Chunk keyFramerInfoChunk     = new KeyFramerInfoChunk();
+        Chunk spotLightChunk         = new SpotLightChunk();
+        Chunk floatChunk             = new FloatChunk();
+        Chunk framesChunk            = new FramesChunk();
+        Chunk pivotChunk             = new PivotChunk();
+        Chunk positionChunk          = new PositionChunk();
+        Chunk rotationChunk          = new RotationChunk();
+        Chunk scaleChunk             = new ScaleChunk();
+        Chunk hierarchyInfoChunk     = new HierarchyInfoChunk();
+        Chunk boundingBoxChunk       = new BoundingBoxChunk();
+        Chunk stringChunk            = new StringChunk();
+        Chunk globalColorChunk       = new GlobalColorChunk();
+        Chunk booleanChunk           = new BooleanChunk();
+        Chunk percentageChunk        = new PercentageChunk();
+        Chunk cameraChunk            = new CameraChunk();
+        Chunk colorChunk             = new ColorChunk();
+        Chunk vertex3ListChunk       = new Vertex3ListChunk();
+        Chunk vertex2ListChunk       = new Vertex2ListChunk();
+        Chunk axisChunk              = new AxisChunk();
+        Chunk facesMaterialChunk     = new FacesMaterialChunk();
+        Chunk smoothingChunk         = new SmoothingChunk();
+
+
+        //mainChunk.addSubChunk(VERSION, stringChunk);
+        mainChunk.addSubChunk(EDITOR, editorChunk);
+        mainChunk.addSubChunk(KEYFRAMER, keyFramerChunk);
+
+        editorChunk.addSubChunk(MATERIAL, materialChunk);
+        editorChunk.addSubChunk(SCALE, floatChunk);
+        editorChunk.addSubChunk(NAMED_OBJECT, namedObjectChunk);
+
+        keyFramerChunk.addSubChunk(FRAMES_CHUNK, framesChunk);
+        keyFramerChunk.addSubChunk(MESH_INFO, keyFramerInfoChunk);
+        //keyFramerChunk.addSubChunk(AMBIENT_LIGHT_INFO, keyFramerInfoChunk);
+        //keyFramerChunk.addSubChunk(CAMERA_INFO, keyFramerInfoChunk);
+        //keyFramerChunk.addSubChunk(CAMERA_TARGET_INFO, keyFramerInfoChunk);
+        //keyFramerChunk.addSubChunk(OMNI_LIGHT_INFO, keyFramerInfoChunk);
+        //keyFramerChunk.addSubChunk(SPOT_LIGHT_TARGET_INFO, keyFramerInfoChunk);
+        //keyFramerChunk.addSubChunk(SPOT_LIGHT_INFO, keyFramerInfoChunk);
+
+        keyFramerInfoChunk.addSubChunk(NAME_AND_FLAGS, framesDescriptionChunk);
+        keyFramerInfoChunk.addSubChunk(PIVOT, pivotChunk);
+        keyFramerInfoChunk.addSubChunk(POSITION, positionChunk);
+        keyFramerInfoChunk.addSubChunk(ROTATION, rotationChunk);
+        keyFramerInfoChunk.addSubChunk(SCALE_TRACK, scaleChunk);
+        keyFramerInfoChunk.addSubChunk(HIERARCHY_INFO, hierarchyInfoChunk);
+        keyFramerInfoChunk.addSubChunk(BOUNDING_BOX, boundingBoxChunk);
+
+        //spotLightChunk.addSubChunk(LIGHT_OFF, booleanChunk);
+        //spotLightChunk.addSubChunk(RAYTRACE, booleanChunk);
+        //spotLightChunk.addSubChunk(SHADOWED, booleanChunk);
+        //spotLightChunk.addSubChunk(SHOW_CONE, booleanChunk);
+        //spotLightChunk.addSubChunk(RECTANGULAR, booleanChunk);
+        //spotLightChunk.addSubChunk(SHADOW_MAP, booleanChunk);
+        //spotLightChunk.addSubChunk(OVERSHOOT, booleanChunk);
+        //spotLightChunk.addSubChunk(SPOT_MAP, booleanChunk);
+        //spotLightChunk.addSubChunk(SPOT_ROLL, booleanChunk);
+        //spotLightChunk.addSubChunk(RAY_TRACE_BIAS, booleanChunk);
+
+        materialChunk.addSubChunk(MATERIAL_NAME, stringChunk);
+
+        materialChunk.addSubChunk(AMBIENT_COLOR, globalColorChunk);
+        materialChunk.addSubChunk(DIFFUSE_COLOR, globalColorChunk);
+        materialChunk.addSubChunk(SPECULAR_COLOR, globalColorChunk);
+        materialChunk.addSubChunk(TEXTURE, textureChunk);
+
+        materialChunk.addSubChunk(TWO_SIDED, booleanChunk);
+
+        materialChunk.addSubChunk(SHININESS, percentageChunk);
+        materialChunk.addSubChunk(TRANSPARENCY, percentageChunk);
+
+        namedObjectChunk.addSubChunk(MESH, triangularMeshChunk);
+        namedObjectChunk.addSubChunk(CAMERA, cameraChunk);
+        namedObjectChunk.addSubChunk(LIGHT, lightChunk);
+
+        lightChunk.addSubChunk(RANGE_START, floatChunk);
+        lightChunk.addSubChunk(COLOR, colorChunk);
+        lightChunk.addSubChunk(RANGE_END, floatChunk);
+        lightChunk.addSubChunk(MULTIPLIER, floatChunk);
+        lightChunk.addSubChunk(SPOTLIGHT, spotLightChunk);
+
+
+        textureChunk.addSubChunk(TEXTURE_NAME, stringChunk);
+
+        triangularMeshChunk.addSubChunk(VERTEX_LIST, vertex3ListChunk);
+        triangularMeshChunk.addSubChunk(TEXTURE_COORDINATES, vertex2ListChunk);
+        triangularMeshChunk.addSubChunk(FACES_DESCRIPTION, facesDescriptionChunk);
+        triangularMeshChunk.addSubChunk(COORDINATE_AXES, axisChunk);
+
+        facesDescriptionChunk.addSubChunk(FACES_MATERIAL, facesMaterialChunk);
+        facesDescriptionChunk.addSubChunk(SMOOTH, smoothingChunk);
+
+        /*
+           put(new Integer((short)0x0010), "Rgb (float)");
+           put(new Integer((short)0x0011), "Rgb (byte)");
+           put(new Integer((short)0x0012), "Rgb (byte) gamma corrected");
+           put(new Integer((short)0x0013), "Rgb (float) gamma corrected");
+           put(new Integer((short)0x0030), "percent (int)");
+           put(new Integer((short)0x0031), "percent (float)");
+           put(new Integer((short)0x0002), "3DS-Version");
+           put(new Integer((short)0x3D3D), "3D editor chunk");
+           put(new Integer((short)0x0100), "One unit");
+           put(new Integer((short)0x1100), "Background bitmap");
+           put(new Integer((short)0x1101), "Use background bitmap");
+           put(new Integer((short)0x1200), "Background color");
+           put(new Integer((short)0x1201), "Use background color");
+           put(new Integer((short)0x1300), "Gradient colors");
+           put(new Integer((short)0x1301), "Use gradient");
+           put(new Integer((short)0x1400), "Shadow map bias");
+           put(new Integer((short)0x1420), "Shadow map size");
+           put(new Integer((short)0x1450), "Shadow map sample range");
+           put(new Integer((short)0x1460), "Raytrace bias");
+           put(new Integer((short)0x1470), "Raytrace on");
+           put(new Integer((short)0x2100), "Ambient color");
+           put(new Integer((short)0x2200), "Fog");
+           put(new Integer((short)0x2210), "fog background");
+           put(new Integer((short)0x2201), "Use fog");
+           put(new Integer((short)0x2210), "Fog background");
+           put(new Integer((short)0x2300), "Distance queue");
+           put(new Integer((short)0x2310), "Dim background");
+           put(new Integer((short)0x2301), "Use distance queue");
+           put(new Integer((short)0x2302), "Layered fog options");
+           put(new Integer((short)0x2303), "Use layered fog");
+           put(new Integer((short)0x3D3E), "Mesh version");
+           put(new Integer((short)0x4000), "Object block");
+           put(new Integer((short)0x4010), "Object hidden");
+           put(new Integer((short)0x4012), "Object doesn't cast");
+           put(new Integer((short)0x4013), "Matte object");
+           put(new Integer((short)0x4015), "External process on");
+           put(new Integer((short)0x4017), "Object doesn't receive shadows");
+           put(new Integer((short)0x4100), "Triangular mesh");
+           put(new Integer((short)0x4110), "Vertices list");
+           put(new Integer((short)0x4120), "Faces description");
+           put(new Integer((short)0x4130), "Faces material list");
+           put(new Integer((short)0x4140), "Mapping coordinates list");
+           put(new Integer((short)0x4150), "Smoothing group list");
+           put(new Integer((short)0x4160), "Local coordinate system");
+           put(new Integer((short)0x4165), "Object color in editor");
+           put(new Integer((short)0x4181), "External process name");
+           put(new Integer((short)0x4182), "External process parameters");
+           put(new Integer((short)0x4600), "Light");
+           put(new Integer((short)0x4610), "Spotlight");
+           put(new Integer((short)0x4627), "Spot raytrace");
+           put(new Integer((short)0x4630), "Light shadowed");
+           put(new Integer((short)0x4641), "Spot shadow map");
+           put(new Integer((short)0x4650), "Spot show cone");
+           put(new Integer((short)0x4651), "Spot is rectangular");
+           put(new Integer((short)0x4652), "Spot overshoot");
+           put(new Integer((short)0x4653), "Spot map");
+           put(new Integer((short)0x4656), "Spot roll");
+           put(new Integer((short)0x4658), "Spot ray trace bias");
+           put(new Integer((short)0x4620), "Light off");
+           put(new Integer((short)0x4625), "Attenuation on");
+           put(new Integer((short)0x4659), "Range start");
+           put(new Integer((short)0x465A), "Range end");
+           put(new Integer((short)0x465B), "Multiplier");
+           put(new Integer((short)0x4700), "Camera");
+           put(new Integer((short)0x7001), "Window settings");
+           put(new Integer((short)0x7011), "Window description #2 ...");
+           put(new Integer((short)0x7012), "Window description #1 ...");
+           put(new Integer((short)0x7020), "Mesh windows ...");
+           put(new Integer((short)0xAFFF), "Material block");
+           put(new Integer((short)0xA000), "Material name");
+           put(new Integer((short)0xA010), "Ambient color");
+           put(new Integer((short)0xA020), "Diffuse color");
+           put(new Integer((short)0xA030), "Specular color");
+           put(new Integer((short)0xA040), "Shininess percent");
+           put(new Integer((short)0xA041), "Shininess strength percent");
+           put(new Integer((short)0xA050), "Transparency percent");
+           put(new Integer((short)0xA052), "Transparency falloff percent");
+           put(new Integer((short)0xA053), "Reflection blur percent");
+           put(new Integer((short)0xA081), "2 sided");
+           put(new Integer((short)0xA083), "Add trans");
+           put(new Integer((short)0xA084), "Self illum");
+           put(new Integer((short)0xA085), "Wire frame on");
+           put(new Integer((short)0xA087), "Wire thickness");
+           put(new Integer((short)0xA088), "Face map");
+           put(new Integer((short)0xA08A), "In tranc");
+           put(new Integer((short)0xA08C), "Soften");
+           put(new Integer((short)0xA08E), "Wire in units");
+           put(new Integer((short)0xA100), "Render type");
+           put(new Integer((short)0xA240), "Transparency falloff percent present");
+           put(new Integer((short)0xA250), "Reflection blur percent present");
+           put(new Integer((short)0xA252), "Bump map present (true percent)");
+           put(new Integer((short)0xA200), "Texture map 1");
+           put(new Integer((short)0xA33A), "Texture map 2");
+           put(new Integer((short)0xA210), "Opacity map");
+           put(new Integer((short)0xA230), "Bump map");
+           put(new Integer((short)0xA33C), "Shininess map");
+           put(new Integer((short)0xA204), "Specular map");
+           put(new Integer((short)0xA33D), "Self illum. map");
+           put(new Integer((short)0xA220), "Reflection map");
+           put(new Integer((short)0xA33E), "Mask for texture map 1");
+           put(new Integer((short)0xA340), "Mask for texture map 2");
+           put(new Integer((short)0xA342), "Mask for opacity map");
+           put(new Integer((short)0xA344), "Mask for bump map");
+           put(new Integer((short)0xA346), "Mask for shininess map");
+           put(new Integer((short)0xA348), "Mask for specular map");
+           put(new Integer((short)0xA34A), "Mask for self illum. map");
+           put(new Integer((short)0xA34C), "Mask for reflection map");
+           put(new Integer((short)0xA300), "Mapping filename");
+           put(new Integer((short)0xA351), "Mapping parameters");
+           put(new Integer((short)0xA353), "Blur percent");
+           put(new Integer((short)0xA354), "V scale");
+           put(new Integer((short)0xA356), "U scale");
+           put(new Integer((short)0xA358), "U offset");
+           put(new Integer((short)0xA35A), "V offset");
+           put(new Integer((short)0xA35C), "Rotation angle");
+           put(new Integer((short)0xA360), "RGB Luma/Alpha tint 1");
+           put(new Integer((short)0xA362), "RGB Luma/Alpha tint 2");
+           put(new Integer((short)0xA364), "RGB tint R");
+           put(new Integer((short)0xA366), "RGB tint G");
+           put(new Integer((short)0xA368), "RGB tint B");
+           put(new Integer((short)0xB000), "Key Framer");
+           put(new Integer((short)0xB001), "Ambient light information block");
+           put(new Integer((short)0xB002), "Mesh information block");
+           put(new Integer((short)0xB003), "Camera information block");
+           put(new Integer((short)0xB004), "Camera target information block");
+           put(new Integer((short)0xB005), "Omni light information block");
+           put(new Integer((short)0xB006), "Spot light target information block");
+           put(new Integer((short)0xB007), "Spot light information block");
+           put(new Integer((short)0xB008), "Frames (Start and End)");
+           put(new Integer((short)0xB009), "Current Frame");
+           put(new Integer((short)0xB00A), "Animation revision, filename and length");
+           put(new Integer((short)0xB010), "Object name, parameters and hierarchy father");
+           put(new Integer((short)0xB013), "Object pivot point");
+           put(new Integer((short)0xB014), "Bounding Box");
+           put(new Integer((short)0xB015), "Object morph angle");
+           put(new Integer((short)0xB020), "Position track");
+           put(new Integer((short)0xB021), "Rotation track");
+           put(new Integer((short)0xB022), "Scale track");
+           put(new Integer((short)0xB023), "FOV track");
+           put(new Integer((short)0xB024), "Roll track");
+           put(new Integer((short)0xB025), "Color track");
+           put(new Integer((short)0xB026), "Morph track");
+           put(new Integer((short)0xB027), "Hotspot track");
+           put(new Integer((short)0xB028), "Falloff track");
+           put(new Integer((short)0xB029), "Hide track");
+           put(new Integer((short)0xB030), "Hierarchy position");
+        */
+    }
+
+    }
diff --git a/com/microcrowd/loader/java3d/max3ds/ChunkTester.java b/com/microcrowd/loader/java3d/max3ds/ChunkTester.java
new file mode 100644
index 0000000..55cf0ed
--- /dev/null
+++ b/com/microcrowd/loader/java3d/max3ds/ChunkTester.java
@@ -0,0 +1,31 @@
+/**
+ * Make a donation http://sourceforge.net/donate/index.php?group_id=98797
+ *
+ * Microcrowd.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * Contact Josh DeFord jdeford@microcrowd.com.com
+ */
+
+package com.microcrowd.loader.java3d.max3ds;
+
+/**
+ * Used to test and create test data for chunks.
+ */
+public class ChunkTester
+{
+    
+}
diff --git a/com/microcrowd/loader/java3d/max3ds/DefaultTextureImageLoader.java b/com/microcrowd/loader/java3d/max3ds/DefaultTextureImageLoader.java
new file mode 100644
index 0000000..639e01f
--- /dev/null
+++ b/com/microcrowd/loader/java3d/max3ds/DefaultTextureImageLoader.java
@@ -0,0 +1,103 @@
+/**
+ * Make a donation http://sourceforge.net/donate/index.php?group_id=98797
+ *
+ * Microcrowd.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * Contact Josh DeFord jdeford@realvue.com
+ */
+package com.microcrowd.loader.java3d.max3ds;
+
+import java.awt.Image;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.net.URL;
+import javax.imageio.ImageIO;
+
+/**
+ * General purpose implementation of TextureImageLoader.
+ * Gets the base path from loader and prepends it to the
+ * file name to load an image.
+ */
+public class DefaultTextureImageLoader implements TextureImageLoader
+{
+
+    private Loader3DS loader;
+
+    /**
+     * Constructs an image loader that will resolve image
+     * locations to the base path of the loader provided.
+     * @param the loader that will specify the base path
+     * used to retrieve images.
+     */
+    public DefaultTextureImageLoader(Loader3DS loader)
+    {
+        this.loader = loader;
+    }
+
+
+    /**
+     * Gets the image to be loaded as a texture.
+     * @param imageName the name of the image to load.
+     * @return image to be used.
+     */
+    public Image getTextureImage(String imageName)
+    {
+        File file = null;
+        InputStream in = null;
+        if(loader.fromUrl())
+        {
+            try 
+            {
+                in = new URL(loader.getBaseUrl() + imageName).openStream();
+                return ImageIO.read(in);
+            }
+            catch (FileNotFoundException e)
+            {
+                throw new IllegalArgumentException(" Can't load texture: " + imageName + 
+                        " Make sure it is located in the " +
+                        " same server and directory with the model file."+
+                        " the loader's base path is: " + loader.getBaseUrl());
+            }
+            catch (Exception e)
+            {
+                e.printStackTrace();
+                throw new IllegalArgumentException(" Can't load texture: " + imageName + 
+                        " Make sure it is located in the " +
+                        " same server and directory with the model file."+
+                        " the loader's base path is: " + loader.getBaseUrl());
+            }
+        }
+        else
+        {
+            try 
+            {
+                in = new FileInputStream(new File(loader.getBasePath() + imageName));
+                return ImageIO.read(in);
+            }
+            catch (Exception e)
+            {
+                e.printStackTrace();
+                throw new IllegalArgumentException(" Can't load texture: " + imageName + 
+                        " Make sure it is located in the " +
+                        " same server and directory with the model file."+
+                        " the loader's base path is: " + loader.getBasePath());
+            }
+        }
+    }
+}
diff --git a/com/microcrowd/loader/java3d/max3ds/Loader3DS.java b/com/microcrowd/loader/java3d/max3ds/Loader3DS.java
new file mode 100644
index 0000000..2246ac1
--- /dev/null
+++ b/com/microcrowd/loader/java3d/max3ds/Loader3DS.java
@@ -0,0 +1,317 @@
+/**
+ * Make a donation http://sourceforge.net/donate/index.php?group_id=98797
+ * Microcrowd.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * Contact Josh DeFord jdeford@microcrowd.com
+ */
+
+package com.microcrowd.loader.java3d.max3ds;
+
+import java.awt.Image;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import javax.media.j3d.BoundingSphere;
+import javax.media.j3d.Group;
+import javax.media.j3d.PointLight;
+import javax.media.j3d.Transform3D;
+import javax.media.j3d.TransformGroup;
+import javax.vecmath.Point3d;
+import com.sun.j3d.loaders.LoaderBase;
+import com.sun.j3d.loaders.Scene;
+import com.sun.j3d.loaders.SceneBase;
+
+/**
+ * Used to load a 3ds studio max file. This will sequentially read a 3ds file,
+ * load or skip chunks and subchunks and initialize the data for the chunks.
+ * A {@link ChunkChopper} is a singleton flyweight factory responsible for
+ * chopping the data up and sending it to the  corresponding chunks(which are
+ * flyweights ala the flyweight pattern) for processing.
+ * 
+ * <p>
+ * Features not supported; unknown chunks are skipped.
+ * </p>
+ */
+public class Loader3DS extends LoaderBase
+{
+    private boolean dataMapInitialized;
+
+    private TextureImageLoader textureImageLoader;
+    private boolean fromUrl;
+
+    public Loader3DS()
+    {
+        //turnOnDebug();
+    }
+
+    /**
+     * Setting this will initialize a lot of debugging code that has lots of
+     * overhead.
+     */
+    private boolean debugMode;
+
+    /**
+     * This is not supported
+     *
+     * @param reader loads a model from a reader 
+     *
+     * @return nothing, this isn't implemented. 
+     *
+     * @throws FileNotFoundException 
+     * @throws UnsupportedOperationException 
+     */
+    public Scene load(Reader reader) throws FileNotFoundException
+    {
+        throw new UnsupportedOperationException("Not supported for 3DS");
+    }
+
+    /**
+     * Loads the model by parsing the file, modelPath and  creating a 3D Scene.
+     *
+     * @param modelPath the path of the 3ds file.
+     *
+     * @return a loaded scene 
+     *
+     * @throws FileNotFoundException if the file can't be located. 
+     */
+    public Scene load(String modelPath) throws FileNotFoundException
+    {
+        InputStream fileIn = null;
+        setBasePathFromFilename(modelPath);
+
+        try {
+            File modelFile = getFile(modelPath);
+            fileIn = new FileInputStream(modelFile);
+            return parseChunks(fileIn, (int)modelFile.length());
+        } finally {
+            try {
+                fileIn.close();
+            } catch (Exception e) {
+                e.printStackTrace();
+                //Don't care about exceptions at this point.
+            }
+        }
+    }
+
+    private void setBaseUrlFromUrl(URL url) throws FileNotFoundException
+    {
+        String u = url.toString();
+        String s;
+        if (u.lastIndexOf('/') == -1) {
+            s = url.getProtocol() + ":";
+        } else {
+            s = u.substring(0, u.lastIndexOf('/') + 1);
+        }
+        try {
+            setBaseUrl(new URL(s));
+        }
+        catch (MalformedURLException e) {
+            throw new FileNotFoundException(e.getMessage());
+        }
+    }
+
+    /*
+     * Takes a file name and sets the base path to the directory
+     * containing that file.
+     */
+    private void setBasePathFromFilename(String fileName)
+    {
+        if (fileName.lastIndexOf(java.io.File.separator) == -1) {
+            // No path given - current directory
+            setBasePath("." + java.io.File.separator);
+        } else {
+            setBasePath(fileName.substring(0, fileName.lastIndexOf(java.io.File.separator)));
+        }
+    }
+
+    /**
+     * Set the path where files associated with this .obj file are
+     * located.
+     * Only needs to be called to set it to a different directory
+     * from that containing the .obj file.
+     */
+    public void setBasePath(String pathName)
+    {
+      String basePath = pathName;
+      if (basePath == null || basePath == "")
+	  basePath = "." + java.io.File.separator;
+      basePath = basePath.replace('/', java.io.File.separatorChar);
+      basePath = basePath.replace('\\', java.io.File.separatorChar);
+      if (!basePath.endsWith(java.io.File.separator))
+	  basePath = basePath + java.io.File.separator;
+      super.setBasePath(basePath);
+    }
+
+    /**
+     * Returns true if this loader is loading files
+     * from a url.
+     */
+    public boolean fromUrl()
+    {
+        return fromUrl;
+    }
+
+    /**
+     * gets an image with the specified name.
+     * This uses a DefaultTextureImageLoader
+     * to load the image if one hasn't been set for
+     * this loader.
+     * @param imageName name of image to load.
+     * @return image corresponding to imageName
+     */
+    public Image getTextureImage(String imageName)
+    {
+        try {
+            if(textureImageLoader == null)
+            {
+                textureImageLoader = new DefaultTextureImageLoader(this);
+            }
+            return textureImageLoader.getTextureImage(imageName);
+        } 
+        catch (IllegalArgumentException e)
+        {
+            System.out.println(e.getMessage());
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * Sets the TextureImageLoader to be used
+     * when texture images are needed.
+     * @param loader the TextureImageLoader that will be used to load images.
+     */
+    public void setTextureImageLoader(TextureImageLoader loader)
+    {
+        textureImageLoader = loader;
+    }
+
+    /**
+     * Gets a chunk chopper to do all the dirty work.
+     *
+     * @param inputStream the stream containing the model.
+     * @param modelSize size of the model file. 
+     *
+     * @return a java3d scene built from input. 
+     */
+    protected Scene parseChunks(InputStream inputStream, int modelSize)
+    {
+        ChunkChopper chopper = new ChunkChopper();
+        SceneBase base = chopper.loadSceneBase(inputStream, this, modelSize);
+        if(!chopper.hasLights())
+        {
+            addDefaultLights(base.getSceneGroup());
+            addDefaultLights(chopper.getGroup());
+        }
+        return base;
+    }
+
+    /**
+     * Adds defaultlights to the group provided 
+     * similar to the ones 3ds max adds when there are none in the scene.
+     * @param group to add the lighting to.
+     */
+    public static void addDefaultLights(Group group)
+    {
+        PointLight light1 = new PointLight();
+        PointLight light2 = new PointLight();
+        light1.setInfluencingBounds(new BoundingSphere(new Point3d(0,0,0), 3000));
+        light2.setInfluencingBounds(new BoundingSphere(new Point3d(0,0,0), 3000));
+
+        Transform3D t1 = new Transform3D(new float[]{1.0f, 0.0f, 0.0f, -900f, 
+            0.0f, 1.0f, 0.0f, 1500f, 
+            0.0f, 0.0f, 1.0f, 1000f, 
+            0.0f, 0.0f, 0.0f, 1.0f});
+        Transform3D t2 = new Transform3D(new float[]{1.0f, 0.0f, 0.0f, 900f, 
+            0.0f, 1.0f, 0.0f, -1500f, 
+            0.0f, 0.0f, 1.0f, -1000f, 
+            0.0f, 0.0f, 0.0f, 1.0f});
+        TransformGroup group1 = new TransformGroup(t1);
+        TransformGroup group2 = new TransformGroup(t2);
+        group1.addChild(light1);
+        group2.addChild(light2);
+        group.addChild(group1);
+        group.addChild(group2);
+    }
+
+    /**
+     * Retrieves a file with a given name. 
+     *
+     * @param fileName name of file to retrieve. 
+     *
+     * @return retrieved file. 
+     */
+    private File getFile(String fileName)
+    {
+        File file = null;
+        try {
+            file = new File(fileName);
+            if (!file.exists()) {
+                throw new IOException(fileName + " doesn't exist");
+            }
+        } catch (IOException ioe) {
+            ioe.printStackTrace();
+        }
+
+        return file;
+    }
+
+    /**
+     * throws UnsupportedOperationException
+     *
+     * @param url url of model to be loaded. 
+     *
+     * @return a java3d scene represented in url 
+     *
+     * @throws FileNotFoundException if file couldn't be found. 
+     */
+    public Scene load(URL url) throws FileNotFoundException
+    {
+        fromUrl = true;
+        try {
+            URLConnection connection = url.openConnection();
+            if (baseUrl == null) 
+                setBaseUrlFromUrl(url);
+
+            return parseChunks(connection.getInputStream(), connection.getContentLength());
+        } catch (Exception e) {
+            e.printStackTrace();
+            throw new IllegalArgumentException("Url " + url + " cannot be loaded");
+
+        }
+    }
+
+    /**
+     * Turn on debug mode for all 3ds xml.
+     */
+    public void turnOnDebug()
+    {
+        if (!debugMode) {
+            ChunkChopper.debug = true;
+            debugMode = true;
+        }
+    }
+}
diff --git a/com/microcrowd/loader/java3d/max3ds/Main.java b/com/microcrowd/loader/java3d/max3ds/Main.java
new file mode 100644
index 0000000..191e4b8
--- /dev/null
+++ b/com/microcrowd/loader/java3d/max3ds/Main.java
@@ -0,0 +1,454 @@
+/**
+ * Make a donation http://sourceforge.net/donate/index.php?group_id=98797
+ * Microcrowd.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * Contact Josh DeFord jdeford@microcrowd.com
+ */
+
+package com.microcrowd.loader.java3d.max3ds;
+
+import java.applet.Applet;
+import java.awt.BorderLayout;
+import java.awt.GraphicsDevice;
+import java.awt.GraphicsEnvironment;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import javax.media.j3d.Behavior;
+import javax.media.j3d.BoundingSphere;
+import javax.media.j3d.BranchGroup;
+import javax.media.j3d.Canvas3D;
+import javax.media.j3d.GraphicsConfigTemplate3D;
+import javax.media.j3d.Interpolator;
+import javax.media.j3d.Locale;
+import javax.media.j3d.PhysicalBody;
+import javax.media.j3d.PhysicalEnvironment;
+import javax.media.j3d.RotPosPathInterpolator;
+import javax.media.j3d.Transform3D;
+import javax.media.j3d.TransformGroup;
+import javax.media.j3d.TransformInterpolator;
+import javax.media.j3d.View;
+import javax.media.j3d.ViewPlatform;
+import javax.media.j3d.VirtualUniverse;
+import javax.swing.JFileChooser;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.filechooser.FileFilter;
+import javax.vecmath.Point3d;
+import javax.vecmath.Point3f;
+import javax.vecmath.Quat4f;
+import com.sun.j3d.loaders.Scene;
+import com.sun.j3d.utils.applet.MainFrame;
+import com.sun.j3d.utils.behaviors.keyboard.KeyNavigatorBehavior;
+
+
+/**
+ * Provides an example of loader usage with key navigation.
+ * If loaded as an applet expects a 'url' parameter tag which
+ * must be specified as a fully qualified url.  If there isn't a 
+ * url tag it looks for an applet parameter called filename.
+ * If this is run from the command line it will try to load
+ * the file passed in at the prompt, unless a command line argument
+ * 'url' is the first argument.  Then it will load the url represented
+ * as the second command argument.
+ */
+public class Main extends Applet
+{
+    private BoundingSphere bounds = new BoundingSphere(new Point3d(0.0,0.0,0.0),3000.0);
+    private Canvas3D canvas;
+    private String modelLocation;
+    private static final String DEFAULT_MODEL = "bounce.3DS";
+    private BranchGroup universeBranch;
+
+    /**
+     * If loaded from the command line a modelFile must be provided.
+     * args are <filename> or url <url>
+     */
+    public static void main(String args[])
+    {
+        Main self = new Main();
+        if(args.length > 0)
+            self.modelLocation = args[0];
+
+        MainFrame mainFrame = new MainFrame(self, 750, 550);
+    }
+
+    /**
+     * Initializes applet. If this is loaded from a command
+     * line the model is loaded from the parameters provided.
+     * If it is loaded from an applet the parameter tag 'model'
+     * is checked for a path to a model. 
+     */
+    public void init()
+    {
+        try 
+        {
+            URL location = null;
+            if(modelLocation == null)
+            {
+                location = findAFile(DEFAULT_MODEL);
+            }
+            else
+            {
+                location = findAFile(modelLocation);
+            } 
+
+            if(location == null)
+                throw new IllegalArgumentException("No model was found when attempting to retrieve " +
+                        (modelLocation == null ? DEFAULT_MODEL : modelLocation));
+
+            GraphicsConfigTemplate3D config = new GraphicsConfigTemplate3D();
+            config.setSceneAntialiasing(GraphicsConfigTemplate3D.PREFERRED);
+            GraphicsDevice gd[] = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices();
+            canvas = new Canvas3D(gd[0].getBestConfiguration(config));
+
+            setLayout(new BorderLayout());
+            add("Center", canvas);
+
+            JMenuBar menuBar = new JMenuBar();
+            JMenu fileMenu = new JMenu("File");
+            JMenuItem menuItem = new JMenuItem("Open");
+            add(menuBar, "North");
+            menuBar.add(fileMenu);
+            fileMenu.add(menuItem);
+            menuItem.addActionListener(new BrowseListener());
+
+            View view = new View();
+
+            Scene scene = getScene(location);
+            universeBranch = createUniverse(view, scene);
+            view.addCanvas3D(canvas);
+
+            addSceneToBranch(universeBranch, scene);
+        }
+        catch(Exception e){
+            e.printStackTrace();
+            usage(e.getMessage());
+        }
+    }
+
+    /**
+     * Adds the scene to the branch and turns on the scene's 
+     * behaviors.
+     */
+    private void addSceneToBranch(BranchGroup branch, Scene scene)
+    {
+        turnOnBehaviors(scene.getBehaviorNodes());
+        BranchGroup modelGroup = scene.getSceneGroup();
+        modelGroup.compile();
+        branch.addChild(modelGroup);
+    }
+
+    /**
+     * This is called during initialization of the applet, 
+     */
+    public void Main()
+    {
+    }
+
+    /**
+     * load the scene.
+     */
+    public Scene getScene(URL location)
+    {
+        Scene scene = null;
+
+        try 
+        {
+            return new Loader3DS().load(location);
+        }
+        catch(IOException e){
+            e.printStackTrace();
+            usage(e.getMessage());
+        }
+        return null;
+    }
+
+
+    /**
+     * Turns on all the behaviors provided.
+     * @param bahaviors the behaviors to enable.
+     */
+    public void turnOnBehaviors(Behavior[] behaviors)
+    {
+        if(behaviors == null)
+            return;
+        for(int i=0; i < behaviors.length; i++)
+        {
+
+            behaviors[i].setEnable(true);
+            if(behaviors[i] instanceof Interpolator)
+            {
+                ((Interpolator)behaviors[i]).setSchedulingBounds(new BoundingSphere(new Point3d(), 3000));
+            }
+            if(behaviors[i] instanceof TransformInterpolator)
+            {
+                ((TransformInterpolator)behaviors[i]).getTarget().setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
+            } 
+        }
+    }
+
+    /**
+     * Adds an interpolator that will be added to 
+     * the view transform so that it may be used to
+     * switched cameras later.
+     * @param target the target that the interpolator will operate on
+     */
+    private void addCameraInterpolator(TransformGroup target)
+    {
+        Interpolator cameraInterpolator = new RotPosPathInterpolator(
+                null, target, new Transform3D(), new float[]{0,1}, 
+                new Quat4f[]{new Quat4f(), new Quat4f()}, new Point3f[]{new Point3f(), new Point3f()});
+        cameraInterpolator.setSchedulingBounds(bounds);
+        target.addChild(cameraInterpolator);
+    }
+    /**
+     * Constructs a scene graph.
+     * <ol>
+     * <li>Creates a branch group
+     * <li>Adds the parent transform group to it.
+     * </ol>
+     */
+    public BranchGroup createSceneGraph() {
+        BranchGroup    root           = new BranchGroup();
+        TransformGroup parentGroup     = new TransformGroup();
+
+        parentGroup.setCapability(TransformGroup.ENABLE_PICK_REPORTING);
+        parentGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
+        parentGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
+        parentGroup.setCapability(TransformGroup.ALLOW_CHILDREN_READ);
+        parentGroup.setCapability(TransformGroup.ALLOW_CHILDREN_WRITE);
+        parentGroup.setCapability(TransformGroup.ALLOW_CHILDREN_EXTEND);
+
+        root.addChild(parentGroup);
+
+        return root;
+    } 
+
+    /**
+     * Creates a universe with a locale turns the scene graph.  
+     * Builds a scene branch group and adds it to the locale.  
+     * Builds a view platform that uses the provided view and adds
+     * that to the locale.
+     * @param view the view to use in the view platform
+     * @param scene the scene to grab a camera from as a default view
+     *
+     * @return the root group of the scene branch of the universe. This
+     * is what other groups for display are added to. 
+     */
+    public BranchGroup createUniverse(View view, Scene scene) 
+    {
+        VirtualUniverse universe         = new VirtualUniverse();
+        Locale locale           = new Locale(universe);
+        BranchGroup     sceneBranch = createSceneGraph();
+        BranchGroup viewBranchGroup  = new BranchGroup();
+        sceneBranch.setCapability(BranchGroup.ALLOW_CHILDREN_EXTEND);
+        viewBranchGroup.setCapability(BranchGroup.ALLOW_CHILDREN_EXTEND);
+        ViewPlatform    platform         = new ViewPlatform();
+
+        TransformGroup viewTransformGroup = new TransformGroup();
+        viewTransformGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
+        viewTransformGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
+        viewTransformGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
+
+        TransformGroup[] viewGroups = scene.getViewGroups();
+        Transform3D viewTransform = new Transform3D();
+        if(viewGroups != null && viewGroups.length > 0)
+        {
+            viewGroups[0].getTransform(viewTransform);
+        }
+
+        addViewKeyBehavior(viewTransformGroup);
+        viewTransformGroup.setTransform(viewTransform);
+
+        addCameraInterpolator(viewTransformGroup);
+        viewTransformGroup.addChild(platform);
+        viewBranchGroup.addChild(viewTransformGroup);
+
+        platform.setViewAttachPolicy(View.RELATIVE_TO_FIELD_OF_VIEW);
+        platform.setActivationRadius(100);
+
+        configureView(platform, view);
+        sceneBranch.setCapability(BranchGroup.ALLOW_DETACH);
+
+        viewBranchGroup.compile();
+        sceneBranch.compile();
+        locale.addBranchGraph(viewBranchGroup);
+        locale.addBranchGraph(sceneBranch);
+        return sceneBranch;
+    }
+
+    /**
+     * Adds a behavior to the view that listens to the canvas.
+     * This allows 1st person navigation.
+     */
+    public void addViewKeyBehavior(TransformGroup viewTransformGroup)
+    {
+        KeyNavigatorBehavior keyBehavior = new KeyNavigatorBehavior(canvas, viewTransformGroup);
+        keyBehavior.setSchedulingBounds(bounds);
+        //keyBehavior.setMovementRate(100.0f);
+        viewTransformGroup.addChild(keyBehavior);
+    }
+
+    /**
+     * Creates a physical environment and physical body and
+     * adds it them the view which is configured to use
+     * a regular screen display for configuration. The view is
+     * attached to the platform.
+     * @param platform the platform which will have the view attached to it.
+     * @param view the view to which the body and environment will be added.
+     * canvas 3d added to it.
+     */
+    protected void configureView(ViewPlatform platform, View view)
+    {
+        PhysicalBody        body        = new PhysicalBody();
+        PhysicalEnvironment environment = new PhysicalEnvironment();
+
+        view.setPhysicalEnvironment(environment);
+        view.setPhysicalBody(body);
+        view.attachViewPlatform(platform);
+        view.setBackClipDistance(1000.0);
+        view.setFrontClipDistance(1.0);
+
+
+    }
+
+    public static void usage()
+    {
+        usage("");
+    }
+
+    public static void usage(String message)
+    {
+        System.out.println(message);
+        System.out.println("This is a sample program for the java3ds loader");
+        System.out.println("usage java -jar Loader3DS <model> where model is the 3ds file");
+        System.out.println("Textures for the file should be in the same directory as the model");
+        System.out.println("If this is being run as an applet a parameter named model (a relative url) may be provided");
+        System.exit(1);
+    }
+
+    /** 
+     * Looks for a file to load.  If one
+     * cannot be found looks for and loads 
+     * the default one.
+     * @param name, or path of the file to find
+     * looks for the following:
+     * <ol>
+     * <li>a file with the path of fileName
+     * <li>a resource in the classpath with fileName
+     * <li>a resource in the classpath corresponding to 
+     * the default file.
+     * <li>a url specified in the applet parameter &quot;model&quot;
+     * </ol>
+     *
+     **/
+    private URL findAFile(String fileName)
+    {
+        URL location = null;
+        if(fileName != null)
+        {
+            try 
+            {
+                File file = new File(fileName);
+                if (file.exists())
+                {
+                    return file.toURL();
+                }
+            }
+            catch(IOException e)
+            {
+                e.printStackTrace();
+                usage(e.getMessage());
+            }
+        }
+
+        if(fileName != null)
+        {
+            location = Main.class.getClassLoader().getResource(fileName);
+        }
+        if(location != null)
+        {
+            return location;
+        }
+
+        String relativeURL = getParameter("model");
+        if(relativeURL != null)
+        {
+            try 
+            {
+                URL codeBase = getCodeBase();
+                return new URL(codeBase.toString() + relativeURL);
+            }
+            catch(MalformedURLException e){
+                e.printStackTrace();
+                usage(e.getMessage());
+            }
+        }
+
+
+        return location;
+    }
+
+    private class BrowseListener implements ActionListener
+    {
+        public void actionPerformed(ActionEvent event)
+        {
+            File file = null;
+            try {
+                JFileChooser chooser = new JFileChooser();
+                Filter3DS filter = new Filter3DS();
+                chooser.setFileFilter(filter);
+                chooser.showDialog(Main.this, "wee");
+                file = chooser.getSelectedFile();
+                if(file == null)
+                    return;
+                Scene scene = getScene(file.toURL());
+                addSceneToBranch(universeBranch, scene);
+            }
+            catch(Exception e){
+                e.printStackTrace();
+                System.out.println("file not loadable " + file);
+            }
+        }
+    }
+
+    private class Filter3DS extends FileFilter
+    {
+        public String getDescription()
+        {
+            return "3DS Files";
+        }
+        public boolean accept(File file)
+        {
+            if (file.isDirectory())
+                return true;
+            String fileName = file.getName();
+            if(fileName.length() < 4)
+                return false;
+            
+            String extension = fileName.substring(fileName.length() - 3, fileName.length());
+            if(extension.equalsIgnoreCase("3ds"))
+                return true;
+            return false;
+        }
+    }
+}
diff --git a/com/microcrowd/loader/java3d/max3ds/TextureImageLoader.java b/com/microcrowd/loader/java3d/max3ds/TextureImageLoader.java
new file mode 100644
index 0000000..17a4daf
--- /dev/null
+++ b/com/microcrowd/loader/java3d/max3ds/TextureImageLoader.java
@@ -0,0 +1,42 @@
+/**
+ * Make a donation http://sourceforge.net/donate/index.php?group_id=98797
+ * Microcrowd.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * Contact Josh DeFord jdeford@microcrowd.com
+ */
+package com.microcrowd.loader.java3d.max3ds;
+
+import java.awt.Image;
+
+/**
+ * Interface specifying a lookup for images to be loaded
+ * in textures.  Implementations using this loader may need
+ * to use different methods to resolve images.  In these
+ * cases a class should be created that implements
+ * this interface. For general purpose applications
+ * DefaultTextureImageLoader should be used.
+ */
+public interface TextureImageLoader
+{
+
+    /**
+     * Gets the image to be loaded as a texture.
+     * @param imageName the name of the image with which it will be
+     * looked up.
+     */
+    public Image getTextureImage(String imageaName) throws Exception;
+}
diff --git a/com/microcrowd/loader/java3d/max3ds/chunks/AxisChunk.java b/com/microcrowd/loader/java3d/max3ds/chunks/AxisChunk.java
new file mode 100644
index 0000000..bd800d4
--- /dev/null
+++ b/com/microcrowd/loader/java3d/max3ds/chunks/AxisChunk.java
@@ -0,0 +1,83 @@
+/**
+ * Make a donation http://sourceforge.net/donate/index.php?group_id=98797
+ * Microcrowd
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * Contact Josh DeFord jdeford@microcrowd.com
+ */
+
+package com.microcrowd.loader.java3d.max3ds.chunks;
+
+import javax.media.j3d.Transform3D;
+import javax.vecmath.Point3f;
+import com.microcrowd.loader.java3d.max3ds.ChunkChopper;
+
+/**
+ * Extracts the local coordinate that will act 
+ * as a shape's axis that will be used by the mesh 
+ * info chunk.
+ */
+public class AxisChunk extends Chunk
+{
+    private float value;
+
+    /**
+     * Loads the local coordinate system for the current mesh.
+     *
+     * @param chopper the ChunkChopper containing the state of the parser. 
+     *
+     * The location of the local coordinate system is defined relative to 
+     * positions and rotations at frame 0.  But the orientation is absolutely
+     * defined.
+     *
+     * With either 3x3 or 4x4 rotation, translation, there
+     * is a simple relationship between each matrix and the resulting coordinate
+     * system. The first three columns of the matrix define the direction vector of the
+     * X, Y and Z axii respectively.
+     * If a 4x4 matrix is defined as:
+     * <code>
+     *     | A B C D |
+     * M = | E F G H |
+     *     | I J K L |
+     *     | M N O P |
+     * </code>
+     * Then the direction vector for each axis is as follows:
+     *
+     * <code>
+     * X-axis = [ A E I ]
+     * Y-axis = [ B F J ]
+     * Z-axis = [ C G K ]
+     * </code>
+     *
+     * @return the actual number of bytes read.  
+     */
+    public void loadData(ChunkChopper chopper)
+    {
+        Point3f xAxis  = new Point3f();
+        xAxis  = chopper.getPoint();
+        Point3f zAxis  = chopper.getPoint();
+        Point3f yAxis  = chopper.getPoint();
+        Point3f origin = chopper.getPoint();
+
+        Transform3D transform = new Transform3D(new double[]{
+            xAxis.x,  xAxis.y,  xAxis.z, origin.x, 
+            yAxis.x,  yAxis.y,  yAxis.z, origin.y,  
+            -zAxis.x,  -zAxis.y,  -zAxis.z, origin.z,
+            0,0,0,1});
+        String meshName = chopper.getObjectName();
+        chopper.getKeyFramer().setCoordinateSystem(meshName, transform);
+    }
+}
diff --git a/com/microcrowd/loader/java3d/max3ds/chunks/BooleanChunk.java b/com/microcrowd/loader/java3d/max3ds/chunks/BooleanChunk.java
new file mode 100644
index 0000000..2a27f7c
--- /dev/null
+++ b/com/microcrowd/loader/java3d/max3ds/chunks/BooleanChunk.java
@@ -0,0 +1,46 @@
+/**
+ * Make a donation http://sourceforge.net/donate/index.php?group_id=98797
+ * Microcrowd.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * Contact Josh DeFord jdeford@microcrowd.com
+ */
+
+package com.microcrowd.loader.java3d.max3ds.chunks;
+
+import com.microcrowd.loader.java3d.max3ds.ChunkChopper;
+
+/**
+ * A boolean chunk is true if it is present otherwise
+ * there is no chunk and that represents false. This chunk
+ * will set chopper data to Boolean true with a key that is the id of the chunk.
+ * These have no subchunks. Only String data.
+ */
+public class BooleanChunk extends Chunk
+{
+    /**
+     * If this method is being called then 
+     * a boolean true will be set on the chunk chopper
+     * with a key that is the id of this chunk.
+     *
+     * @param chopper the chopper on which the boolean true data is to be set 
+     * 
+     */
+    public void loadData(ChunkChopper chopper)
+    {
+        chopper.pushData(chopper.getID(), new Boolean(true));
+    }
+}
diff --git a/com/microcrowd/loader/java3d/max3ds/chunks/BoundingBoxChunk.java b/com/microcrowd/loader/java3d/max3ds/chunks/BoundingBoxChunk.java
new file mode 100644
index 0000000..e7ca85c
--- /dev/null
+++ b/com/microcrowd/loader/java3d/max3ds/chunks/BoundingBoxChunk.java
@@ -0,0 +1,55 @@
+/**
+ * Make a donation http://sourceforge.net/donate/index.php?group_id=98797
+ * Microcrowd.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * Contact Josh DeFord jdeford@microcrowd.com
+ */
+
+package com.microcrowd.loader.java3d.max3ds.chunks;
+
+import javax.media.j3d.BoundingBox;
+import javax.vecmath.Point3d;
+import javax.vecmath.Point3f;
+import com.microcrowd.loader.java3d.max3ds.ChunkChopper;
+
+/**
+ * Loads the bounding box for keyframer of mesh. The pivot
+ * is relative to it.
+ * {@see KeyFramerInfoChunk} for more information about using
+ * animations from a 3ds file
+ */
+public class BoundingBoxChunk extends Chunk
+{
+    /**
+     * Gets the bounding box and associates it with the current mes.
+     * @param chopper the ChunkChopper containing the state of the parser.  
+     */
+    public void loadData(ChunkChopper chopper)
+    {
+        Point3f min = chopper.getPoint();
+        Point3f max = chopper.getPoint();
+        BoundingBox box = new BoundingBox(new Point3d(min), new Point3d(max));
+
+        Point3f center = new Point3f(max.x - min.x, 
+                max.y - min.y, 
+                max.z - min.z);
+
+            //chopper.getKeyFramer().setBoundingBox(box);
+            chopper.getKeyFramer().setPivotCenter(center);
+    }
+
+}
diff --git a/com/microcrowd/loader/java3d/max3ds/chunks/CameraChunk.java b/com/microcrowd/loader/java3d/max3ds/chunks/CameraChunk.java
new file mode 100644
index 0000000..0044347
--- /dev/null
+++ b/com/microcrowd/loader/java3d/max3ds/chunks/CameraChunk.java
@@ -0,0 +1,65 @@
+/**
+ * Make a donation http://sourceforge.net/donate/index.php?group_id=98797
+ * Microcrowd.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * Contact Josh DeFord jdeford@microcrowd.com
+ */
+
+package com.microcrowd.loader.java3d.max3ds.chunks;
+
+import javax.media.j3d.Transform3D;
+import javax.media.j3d.TransformGroup;
+import javax.vecmath.Point3d;
+import javax.vecmath.Vector3d;
+import com.microcrowd.loader.java3d.max3ds.ChunkChopper;
+/**
+ * Loads percentage values from binary data representing them.
+ */
+public class CameraChunk extends Chunk
+{
+    /**
+     * Reads the position and target vectors and subtracts them to get 
+     * an axis of rotation. Translate a transform to position and rotate
+     * on the axis of rotation to point at the target.   The angle 
+     * between the z axis and the axis of rotation is the angle used to
+     * rotate.  The translated and rotated vector is stored it the
+     * chopper as a named object since camera chunks are named. 
+     */
+    public void loadData(ChunkChopper chopper) 
+    {
+        Vector3d yVector  = new Vector3d(0,1,0);
+        Point3d position = new Point3d(chopper.getPoint());
+        Point3d target   = new Point3d(chopper.getPoint());
+        float    bank     = chopper.getFloat();
+        float    lens     = chopper.getFloat();
+
+        //This is the vector for the direction
+        //of the camera. Represented as a line 
+        //from target to position.  We'll use it
+        //as an axis when we bank.
+        //Vector3d cameraDirection = new Vector3d();
+        //cameraDirection.sub(target,position);
+        //AxisAngle4f bankAxisAngle = new AxisAngle4f(cameraDirection, bankAngle);
+
+
+        Transform3D transform = new Transform3D();
+        transform.lookAt(position, target, yVector);
+        transform.invert();
+        ((TransformGroup)chopper.getGroup()).setTransform(transform);
+        chopper.addViewGroup(chopper.getGroup());
+    }  
+}
diff --git a/com/microcrowd/loader/java3d/max3ds/chunks/Chunk.java b/com/microcrowd/loader/java3d/max3ds/chunks/Chunk.java
new file mode 100644
index 0000000..a25bae7
--- /dev/null
+++ b/com/microcrowd/loader/java3d/max3ds/chunks/Chunk.java
@@ -0,0 +1,139 @@
+/**
+ * Make a donation http://sourceforge.net/donate/index.php?group_id=98797
+ * Microcrowd.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * Contact Josh DeFord jdeford@microcrowd.com
+ */
+
+package com.microcrowd.loader.java3d.max3ds.chunks;
+
+import java.util.HashMap;
+import com.microcrowd.loader.java3d.max3ds.ChunkChopper;
+
+/**
+ * The base class for all chunks. Chunks are  flyweights and should be managed
+ * by  {@link ChunkChopper}  Every chunk should know how many bytes of data(or
+ * be able figure out)  to read before its subchunks are found. Chunks that only have
+ * subchunks need not overrided loadData.
+ * Chunks may store data for later use in loadData with {@link pushData}
+ * If a chunk needs to initialize components with from subchunks, that data
+ * can be retrieved with  {@link ChunkChopper#popData} inside the  {@link
+ * #initialize} method.  {@link #loadData} is called at the beginning of the
+ * loading process, before any data or subchunks have been loaded(the chunk
+ * itself must load its data).
+ * <p>
+ * During loadData, if the length of data before subchunks is unknown,
+ * {@link ChunkChopper#getChunkBytes} may be called and all the data
+ * for the chunk(including subchunks) will be read and returned.
+ */
+public class Chunk 
+{
+    private HashMap subChunkMap = new HashMap();
+    private String name;
+    private String description;
+
+    /**
+     * default no-arg constructror.
+     */
+    public Chunk(String chunkName)
+    {
+        name = chunkName;
+    }
+
+    /**
+     * default no-arg constructror.
+     */
+    public Chunk()
+    {
+    }
+
+    public void addSubChunk(Integer id, Chunk subChunk)
+    {
+        subChunkMap.put(id, subChunk);
+    }
+
+    public Chunk getSubChunk(Integer id)
+    {
+        return (Chunk)subChunkMap.get(id);
+    }
+
+    /**
+     * This method is called after all the  current chunks subchunks are
+     * loaded. Any data stored by those subchunks may be retrieved via {@link
+     * ChunkChopper#popData}
+     * The default implementation does nothing.
+     *
+     * @param chopper may contain data loaded by subchunks. 
+     */
+    public void initialize(ChunkChopper chopper) 
+    {
+    }
+
+    /**
+     * This is called by the chunk chopper before any of the chunk's 
+     * subchunks  are loaded.  Any data loaded that may need to be 
+     * used later by superchunks should be stored in
+     * the chunk chopper via {@link ChunkChopper#popData}
+     * The default implementation does nothing.
+     * @param chopper may contain data loaded by subchunks. 
+     */
+    public void loadData(ChunkChopper chopper)
+    {
+    }
+
+    /**
+     * Sets  nice human readable name for the chunk.
+     * @param name to use for display of this chunk.
+     */
+    public final void setName(String name)
+    {
+        this.name = name;
+    }
+
+    /**
+     * Gets a human readable name for the chunk.
+     * @return name used to display the chunk.
+     */
+    public final String getName()
+    {
+        return name;
+    }
+
+    public final String getDescription()
+    {
+        return description;
+    }
+
+    public final void setDescription(String desc)
+    {
+        description = desc;
+    }
+
+    /**
+     * Returns the name of this chunk.
+     * If the name is null then it just
+     * returns the unqualified class name.
+     */
+    public String toString()
+    {
+        if (getName() != null)
+            return getName();
+        String className = getClass().getName();
+        return className.substring(className.lastIndexOf('.') + 1, 
+            className.length()); 
+    }
+}
diff --git a/com/microcrowd/loader/java3d/max3ds/chunks/ColorChunk.java b/com/microcrowd/loader/java3d/max3ds/chunks/ColorChunk.java
new file mode 100644
index 0000000..c6e24df
--- /dev/null
+++ b/com/microcrowd/loader/java3d/max3ds/chunks/ColorChunk.java
@@ -0,0 +1,82 @@
+/**
+ * Make a donation http://sourceforge.net/donate/index.php?group_id=98797
+ * Microcrowd.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * Contact Josh DeFord jdeford@microcrowd.com
+ */
+
+package com.microcrowd.loader.java3d.max3ds.chunks;
+
+import javax.vecmath.Color3f;
+import com.microcrowd.loader.java3d.max3ds.ChunkChopper;
+
+/**
+ * Loads colors from binary data representing them.
+ */
+public class ColorChunk extends Chunk
+{
+    protected int currentColorType;
+
+    private static final int FLOAT_COLOR = 0x10;
+    private static final int BYTE_COLOR = 0x11;
+    private static final int BYTE_COLOR_GAMMA = 0x12;
+    private static final int FLOAT_COLOR_GAMMA = 0x13;
+
+    /**
+     * Based on the color type retrieved
+     * from {@link #getColorType} loads
+     * an rgb or float color and pushes
+     * it onto the chunk chopper.
+     *
+     * @param chopper the chopper that will store the color data.  
+     */
+    public void loadData(ChunkChopper chopper)
+    {
+        int colorType = getColorType(chopper);
+        if (colorType == BYTE_COLOR) 
+        {
+            float r = (chopper.getUnsignedByte()) / 255f;
+            float g = (chopper.getUnsignedByte()) / 255f;
+            float b = (chopper.getUnsignedByte()) / 255f;
+            Color3f color = new Color3f(r, g, b);
+            chopper.pushData(chopper.getID(), color);
+        } 
+        else if (colorType == FLOAT_COLOR) 
+        {
+            Color3f color = new Color3f(chopper.getFloat(), chopper.getFloat(), chopper.getFloat());
+            chopper.pushData(chopper.getID(), color);
+        }
+        else 
+        {
+            throw new IllegalArgumentException("Only RGB colors are enabled. ChunkID=" 
+                    + Integer.toHexString((byte)chopper.getID().intValue()) 
+                    + " Color type = " + Integer.toHexString((byte)colorType));
+        }
+    }
+
+    /**
+     * Gets the color type for this chunk.
+     * @param chopper with the information the
+     * chunk may use to determine color type
+     * @return the color type for the chunk retrieved
+     * from the chopper using this chunks id.
+     */
+    protected int getColorType(ChunkChopper chopper)
+    {
+        return chopper.getID().intValue();
+    }
+}
diff --git a/com/microcrowd/loader/java3d/max3ds/chunks/FacesDescriptionChunk.java b/com/microcrowd/loader/java3d/max3ds/chunks/FacesDescriptionChunk.java
new file mode 100644
index 0000000..12cad0a
--- /dev/null
+++ b/com/microcrowd/loader/java3d/max3ds/chunks/FacesDescriptionChunk.java
@@ -0,0 +1,322 @@
+/**
+ * Make a donation http://sourceforge.net/donate/index.php?group_id=98797
+ * Microcrowd.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * Contact Josh DeFord jdeford@microcrowd.com
+ */
+
+package com.microcrowd.loader.java3d.max3ds.chunks;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import javax.media.j3d.Appearance;
+import javax.media.j3d.Geometry;
+import javax.media.j3d.Material;
+import javax.media.j3d.Shape3D;
+import javax.media.j3d.TransformGroup;
+import javax.vecmath.Color3f;
+import javax.vecmath.Point3f;
+import javax.vecmath.TexCoord2f;
+import javax.vecmath.Vector3f;
+import com.microcrowd.loader.java3d.max3ds.ChunkChopper;
+import com.microcrowd.loader.java3d.max3ds.ChunkMap;
+import com.sun.j3d.utils.geometry.GeometryInfo;
+import com.sun.j3d.utils.geometry.NormalGenerator;
+import com.sun.j3d.utils.geometry.Stripifier;
+
+/**
+ * This chunk describes all the triangles that make up a mesh.
+ * Each triangle is defined in terms of three indexes each of which
+ * is a point reference to a vertex in the vertex list loaded
+ * by the triangular mesh chunk.
+ * After loading the Smoothing chunk the normals for the mesh
+ * are generated accordingly.
+ */
+public class FacesDescriptionChunk extends Chunk
+{
+    public static final Appearance DEFAULT_APPEARANCE;
+
+    private Point3f[] currentVertices;
+    private TexCoord2f[] textureTriangles;
+    private PointMapper shareMap;
+
+    static {
+        DEFAULT_APPEARANCE= new Appearance();
+        Material defaultMaterial = new Material();
+        defaultMaterial.setAmbientColor(new Color3f(.5f, .5f, .5f));
+        //defaultMaterial.setDiffuseColor(new Color3f(.5f, .5f, .5f));
+        //defaultMaterial.setSpecularColor(new Color3f(.5f, .5f, .5f));
+        DEFAULT_APPEARANCE.setMaterial(defaultMaterial);
+    }
+
+
+
+    /**
+     * Maintains a two way mapping between coordinates
+     * and vertices.  A coordinate to vertex is one to many 
+     * Vertex to coordinate is one to one.
+     * In this class we maintain the definition that a coordinate
+     * is a point in 3D space and a vertex is a coordinate serving
+     * as one of three defining a face.
+     */
+    private class PointMapper extends HashMap
+    {
+        private Set[] coordinateSet;
+        /**
+         * Constructs a PointMapper with a
+         * the number of coordinates initialized to size.
+         * @param size the number of coordinates in the set.
+         */
+        public PointMapper(int size)
+        {
+            coordinateSet = new Set[size];
+        }
+
+        /**
+         * Adds an index for a coordinate to the set of vertices mapped
+         * to that coordinate. All coordinates may have one or more vertices
+         * that use them.  
+         * @param coordinate the coordinate being mapped to the vertexNum 
+         * @param vertexNum the number of the vertex using the coordinate
+         */
+        public void addCoordinate(Point3f coordinate, int vertexNum)
+        {
+            Set sharedCoordinates = (Set)get(coordinate); 
+            if(sharedCoordinates == null)
+            {
+                sharedCoordinates = new HashSet();
+                put(coordinate, sharedCoordinates);
+            }
+            sharedCoordinates.add(new Integer(vertexNum));
+            coordinateSet[vertexNum] = sharedCoordinates;
+        }
+
+        /**
+         * Gets all the coordinates for a particular vertex that
+         * also share that vertex after the smoothing groups have been
+         * accounted for.  Any coordinates that are not both shared
+         * by the vertex and do not share a smoothing group with the coordinate
+         * will not be returned.
+         * @param coordinateNum the number of the coordinate to get the set
+         * of vertices for that share it.
+         * @param smoothGroups the group of coordinates used to filter out the 
+         * non-shared vertices.
+         */
+        public Set getSharedCoordinates(int coordinateNum, int[] smoothGroups)
+        {
+            Set returnSet = new HashSet();
+            Set sharingVertices = coordinateSet[coordinateNum];
+            Iterator vertices = sharingVertices.iterator();
+            int coordinateMask = smoothGroups[coordinateNum];
+            while(vertices.hasNext())
+            {
+                Integer vertex = (Integer)vertices.next();
+                int nextMask = smoothGroups[vertex.intValue()];
+                if((nextMask & coordinateMask) != 0)
+                {
+                    returnSet.add(vertex);
+                }
+            }
+            return returnSet; 
+        }
+    }
+
+    /**
+     * Reads the number of faces from the ChunkChopper.
+     * For each face read three shorts representing
+     * indices of vertices loaded by the TriangularMeshChunk
+     *
+     * @param chopper chopper the has the data  
+     */
+    public void loadData(ChunkChopper chopper)
+    {
+        int numFaces = chopper.getUnsignedShort();
+        shareMap = new PointMapper(numFaces*3);
+        Point3f[] coordinates = (Point3f[])chopper.popData(ChunkMap.VERTEX_LIST);
+        TexCoord2f[] texturePoints = (TexCoord2f[])chopper.popData(ChunkMap.TEXTURE_COORDINATES);
+
+        currentVertices = new Point3f[numFaces * 3];
+        chopper.pushData(chopper.getID(), currentVertices);
+        if (texturePoints != null) 
+        {
+            textureTriangles = new TexCoord2f[numFaces * 3];
+        }
+
+        for (int i = 0; i < numFaces; i++) {
+            int vertexIndex = i * 3;
+            int index0 = chopper.getUnsignedShort();
+            int index1 = chopper.getUnsignedShort();
+            int index2 = chopper.getUnsignedShort();
+
+            currentVertices[vertexIndex] = coordinates[index0];
+            currentVertices[vertexIndex + 1] = coordinates[index1];
+            currentVertices[vertexIndex + 2] = coordinates[index2];
+
+            shareMap.addCoordinate(coordinates[index0], vertexIndex);
+            shareMap.addCoordinate(coordinates[index1], vertexIndex+1);
+            shareMap.addCoordinate(coordinates[index2], vertexIndex+2);
+
+
+            if (textureTriangles != null) {
+                textureTriangles[vertexIndex] = texturePoints[index0];
+                textureTriangles[vertexIndex + 1] = texturePoints[index1];
+                textureTriangles[vertexIndex + 2] = texturePoints[index2];
+            }
+
+            //This is a bit masked value that is used to determine which edges are visible... not needed.
+            chopper.getUnsignedShort(); 
+        }
+    }
+
+    /**
+     * Loads a mesh onto the scene graph with the specified data
+     * from subchunks.
+     * If there is no material, this will put a default
+     * material on the shape.
+     */
+    public void initialize(ChunkChopper chopper)
+    {
+        final String materialName = (String)chopper.popData(ChunkMap.FACES_MATERIAL);
+        final int[]  smoothGroups = (int[])chopper.popData(ChunkMap.SMOOTH);
+        Shape3D      shape        = new Shape3D();
+        GeometryInfo geometryInfo = new GeometryInfo(GeometryInfo.TRIANGLE_ARRAY);
+
+        geometryInfo.setCoordinates(currentVertices);
+        TransformGroup transformGroup = (TransformGroup)chopper.getGroup();
+        transformGroup.addChild(shape);
+
+        if (textureTriangles != null) 
+        {
+            geometryInfo.setTextureCoordinateParams(1, 2);
+            geometryInfo.setTextureCoordinates(0, textureTriangles);
+        }
+
+        if(materialName != null)
+        {
+            shape.setAppearance((Appearance)chopper.getNamedObject(materialName));
+        }
+        else
+        {
+            shape.setAppearance(DEFAULT_APPEARANCE);
+        }
+        if(smoothGroups == null)
+        {
+            NormalGenerator normalGenerator = new NormalGenerator();
+            geometryInfo.recomputeIndices();
+            normalGenerator.generateNormals(geometryInfo);
+        }
+        else
+        {
+            Vector3f[] normals = generateNormals(currentVertices);
+            Vector3f[] smoothNormals = smoothNormals(normals, shareMap, smoothGroups);
+            geometryInfo.setNormals(smoothNormals);
+        }
+
+        new Stripifier().stripify(geometryInfo);
+        shape.setGeometry(geometryInfo.getGeometryArray());
+        shape.setCapability(Geometry.ALLOW_INTERSECT);
+        com.sun.j3d.utils.picking.PickTool.setCapabilities(shape, com.sun.j3d.utils.picking.PickTool.INTERSECT_FULL);
+
+        currentVertices=null;
+        textureTriangles=null;
+    }
+
+    /**
+     * Takes all the normals for all the vertices and averages them with
+     * normals with which they share a coordinate and at least one smooth group.
+     * @param currentNormals the normals for each face.
+     * @param sharedPoints the point mapper that will choose which points are 
+     * and which are not shared.
+     * @param smoothGroups the indexed list of group masks loaded by the smooth chunk.
+     * @return normals averaged among the shared vertices in their smoothing groups.
+     */
+    public Vector3f[] smoothNormals(Vector3f[] currentNormals, PointMapper sharedPoints, int[] smoothGroups)
+    {
+        Vector3f[] smoothNormals = new Vector3f[currentNormals.length];
+        for(int i=0; i < currentNormals.length; i++)
+        {
+            Set otherPoints = sharedPoints.getSharedCoordinates(i, smoothGroups);
+            if(otherPoints != null)
+            {
+                Vector3f[] sharedNormals = new Vector3f[otherPoints.size()]; 
+                Iterator pointIterator = otherPoints.iterator();
+                for(int j = 0; j < sharedNormals.length; j++)
+                {
+                    sharedNormals[j] = currentNormals[((Integer)pointIterator.next()).intValue()];
+                }
+                smoothNormals[i] = averageNormals(sharedNormals);
+            }
+            else
+            {
+                smoothNormals[i] = currentNormals[i];
+            }
+        }
+        return smoothNormals;
+    }
+
+    /**
+     * Averages the normals provided in order to provide
+     * smooth, noncreased appearances for meshes.
+     * @param normals the normals that should be averaged
+     * @return a normalized normal that can be used in place
+     * of all the normals provided.
+     */
+    public Vector3f averageNormals(Vector3f[] normals)
+    {
+        Vector3f newNormal = new Vector3f();
+        for(int i=0; i < normals.length; i++)
+        {
+            newNormal.add(normals[i]);
+        }
+        newNormal.normalize();
+        return newNormal;
+    }
+
+    /**
+     * Generates normals for each vertex of each
+     * face that are absolutely normal to the face.
+     * @param point0 The first point of the face
+     * @param point1 The second point of the face
+     * @param point2 The third point of the face
+     * @return the three normals that should be 
+     * used for the triangle represented by the parameters.
+     */
+    private Vector3f[] generateNormals(Point3f points[])
+    {
+        Vector3f[] normals = new Vector3f[points.length];
+        for(int i=0; i < normals.length;)
+        {
+            Vector3f normal    = new Vector3f();
+            Vector3f v1        = new Vector3f();
+            Vector3f v2        = new Vector3f();
+    
+            v1.sub(points[i+1], points[i]);
+            v2.sub(points[i+2], points[i]);
+            normal.cross(v1, v2);
+            normal.normalize();
+
+    
+            normals[i++] = new Vector3f(normal);
+            normals[i++] = new Vector3f(normal);
+            normals[i++] = new Vector3f(normal);
+        }
+
+        return normals;
+    }
+}
diff --git a/com/microcrowd/loader/java3d/max3ds/chunks/FacesMaterialChunk.java b/com/microcrowd/loader/java3d/max3ds/chunks/FacesMaterialChunk.java
new file mode 100644
index 0000000..7fbe4be
--- /dev/null
+++ b/com/microcrowd/loader/java3d/max3ds/chunks/FacesMaterialChunk.java
@@ -0,0 +1,59 @@
+/**
+ * Make a donation http://sourceforge.net/donate/index.php?group_id=98797
+ *
+ * Microcrowd.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * Contact Josh DeFord jdeford@microcrowd.com
+ */
+package com.microcrowd.loader.java3d.max3ds.chunks;
+
+import com.microcrowd.loader.java3d.max3ds.ChunkChopper;
+
+/**
+ * FacesMaterialsChunk contains the materials information 
+ * from the 3ds file. It contains information pertaining to
+ * which faces of a mesh have materials on them and the
+ * texture coordinates for texture mapping.
+ * Right now, its just putting the name of the material
+ * that needs to be applied to the mesh under construction.
+ *
+ * @author jdeford
+ */
+public class FacesMaterialChunk extends Chunk
+{
+    /**
+     * Loads the texture coordinates for a mesh, 
+     *
+     * @param chopper the ChunkChopper containing the state of the parser.  
+     */
+    public void loadData(ChunkChopper chopper)
+    {
+        final String materialName = chopper.getString();
+        int numFaces = chopper.getUnsignedShort();
+        if (numFaces > 0) 
+        {
+            for (int j = 0; j < numFaces; j++) 
+            {
+                int index = j * 3;
+                int position = chopper.getUnsignedShort() * 3;
+            }
+        }
+
+        chopper.pushData(chopper.getID(), materialName);
+    }
+}
+
diff --git a/com/microcrowd/loader/java3d/max3ds/chunks/FloatChunk.java b/com/microcrowd/loader/java3d/max3ds/chunks/FloatChunk.java
new file mode 100644
index 0000000..d408714
--- /dev/null
+++ b/com/microcrowd/loader/java3d/max3ds/chunks/FloatChunk.java
@@ -0,0 +1,47 @@
+/**
+ * Make a donation http://sourceforge.net/donate/index.php?group_id=98797
+ * Microcrowd.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * Contact Josh DeFord jdeford@microcrowd.com
+ */
+
+package com.microcrowd.loader.java3d.max3ds.chunks;
+
+import com.microcrowd.loader.java3d.max3ds.ChunkChopper;
+
+/**
+ * These have no subchunks. Only data.
+ * Any objects that have a float chunk
+ * as a sub chunk may retrieve the float
+ * value from {@link ChunkChopper.popData()}
+ * using the current chunk id as an argument.
+ */
+public class FloatChunk extends Chunk
+{
+    /**
+     * Loads a Float value into the chopper
+     * for use later when parent chunks are 
+     * initializing
+     *
+     * @param chopper the chopper in which the float
+     * chunk will be stored by the id of this chunk. 
+     */
+    public void loadData(ChunkChopper chopper)
+    {
+        chopper.pushData(chopper.getID(), new Float(chopper.getFloat()));
+    }
+}
diff --git a/com/microcrowd/loader/java3d/max3ds/chunks/FramesChunk.java b/com/microcrowd/loader/java3d/max3ds/chunks/FramesChunk.java
new file mode 100644
index 0000000..2677928
--- /dev/null
+++ b/com/microcrowd/loader/java3d/max3ds/chunks/FramesChunk.java
@@ -0,0 +1,45 @@
+/**
+ * Make a donation http://sourceforge.net/donate/index.php?group_id=98797
+ * Microcrowd.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * Contact Josh DeFord jdeford@microcrowd.com
+ */
+
+package com.microcrowd.loader.java3d.max3ds.chunks;
+
+import com.microcrowd.loader.java3d.max3ds.ChunkChopper;
+
+/**
+ * This chunk specifies the beginning and end frames.
+ *
+ * @author jdeford 
+ */
+public class FramesChunk extends Chunk
+{
+    /**
+     * Reads two ints. Start frame and stop frame. 
+     *
+     * @param chopper  the chopper that may be used to store the starting and 
+     * stopping frames.
+     * 
+     */
+    public void loadData(ChunkChopper chopper)
+    {
+        long start = chopper.getUnsignedInt();
+        long stop = chopper.getUnsignedInt();
+    }
+}
diff --git a/com/microcrowd/loader/java3d/max3ds/chunks/FramesDescriptionChunk.java b/com/microcrowd/loader/java3d/max3ds/chunks/FramesDescriptionChunk.java
new file mode 100644
index 0000000..a570ef9
--- /dev/null
+++ b/com/microcrowd/loader/java3d/max3ds/chunks/FramesDescriptionChunk.java
@@ -0,0 +1,59 @@
+/**
+ * Make a donation http://sourceforge.net/donate/index.php?group_id=98797
+ * Microcrowd.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * Contact Josh DeFord jdeford@microcrowd.com
+ */
+
+package com.microcrowd.loader.java3d.max3ds.chunks;
+
+
+import javax.media.j3d.TransformGroup;
+import com.microcrowd.loader.java3d.max3ds.ChunkChopper;
+import com.microcrowd.loader.java3d.max3ds.data.KeyFramer;
+/**
+ * This chunk contains the name of the object
+ * the frames belong to and the parameters and 
+ * hierarchy information for it.
+ */
+public class FramesDescriptionChunk extends Chunk
+{
+    /**
+     * reads the name of the object for these frames
+     * and stores it in the chopper.
+     *
+     * @param chopper the chopper used to store the transient data
+     * for this chunk. 
+     */
+    public void loadData(ChunkChopper chopper)
+    {
+        KeyFramer keyFramer = chopper.getKeyFramer();
+        String objectName = chopper.getString();
+        chopper.setObjectName(objectName);
+        chopper.getUnsignedShort();
+        chopper.getUnsignedShort();
+        int fatherID = chopper.getShort();
+        TransformGroup transformGroup = chopper.getNamedTransformGroup(objectName);
+        if(transformGroup == null)//its a dummy transformGroup.
+        {
+            transformGroup = new TransformGroup();
+            keyFramer.setDummyObject(transformGroup);
+        }
+
+        keyFramer.addFather(fatherID, transformGroup);
+    }
+}
diff --git a/com/microcrowd/loader/java3d/max3ds/chunks/GlobalColorChunk.java b/com/microcrowd/loader/java3d/max3ds/chunks/GlobalColorChunk.java
new file mode 100644
index 0000000..e8b3ed0
--- /dev/null
+++ b/com/microcrowd/loader/java3d/max3ds/chunks/GlobalColorChunk.java
@@ -0,0 +1,45 @@
+/**
+ * Make a donation http://sourceforge.net/donate/index.php?group_id=98797
+ * Microcrowd.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * Contact Josh DeFord jdeford@microcrowd.com
+ */
+
+package com.microcrowd.loader.java3d.max3ds.chunks;
+
+import com.microcrowd.loader.java3d.max3ds.ChunkChopper;
+
+/**
+ * Loads colors from binary data representing them.
+ * Its a global in the manner that it reads its own
+ * header information.
+ */
+public class GlobalColorChunk extends ColorChunk
+{
+    /**
+     * Gets the color type for this chunk.
+     * @param chopper with the information the
+     * chunk may use to determine color type
+     * @return the color type for the chunk.
+     */
+    protected int getColorType(ChunkChopper chopper)
+    {
+        int type = chopper.getUnsignedShort();
+        int colorLength = chopper.getUnsignedInt();
+        return type;
+    }
+}
diff --git a/com/microcrowd/loader/java3d/max3ds/chunks/HierarchyInfoChunk.java b/com/microcrowd/loader/java3d/max3ds/chunks/HierarchyInfoChunk.java
new file mode 100644
index 0000000..a5c48db
--- /dev/null
+++ b/com/microcrowd/loader/java3d/max3ds/chunks/HierarchyInfoChunk.java
@@ -0,0 +1,46 @@
+/**
+ * Make a donation http://sourceforge.net/donate/index.php?group_id=98797
+ * Microcrowd.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * Contact Josh DeFord jdeford@microcrowd.com
+ */
+package com.microcrowd.loader.java3d.max3ds.chunks;
+
+import com.microcrowd.loader.java3d.max3ds.ChunkChopper;
+
+/**
+ * A HierarchyInfoChunk stores information about 
+ * where an object belong in a hierarchy of object that 
+ * have animations which may or may not be related.
+ * Each object, including dummy objects, have an identifier
+ * used to specify them as hierarchical parents of other 
+ * objects for the purpose of key framing.
+ *
+ * @author Josh DeFord 
+ */
+public class HierarchyInfoChunk extends Chunk
+{
+    /**
+     * Loads a word of data that describes the parent. 
+     */
+    public void loadData(ChunkChopper chopper)
+    {
+        int hierarchyIdentifier = chopper.getShort();
+        chopper.getKeyFramer().setID(hierarchyIdentifier);
+    }
+
+}
diff --git a/com/microcrowd/loader/java3d/max3ds/chunks/KeyFramerInfoChunk.java b/com/microcrowd/loader/java3d/max3ds/chunks/KeyFramerInfoChunk.java
new file mode 100644
index 0000000..6d6d6d4
--- /dev/null
+++ b/com/microcrowd/loader/java3d/max3ds/chunks/KeyFramerInfoChunk.java
@@ -0,0 +1,68 @@
+/**
+ * Make a donation http://sourceforge.net/donate/index.php?group_id=98797
+ * Microcrowd.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * Contact Josh DeFord jdeford@microcrowd.com
+ */
+package com.microcrowd.loader.java3d.max3ds.chunks;
+
+import javax.media.j3d.Behavior;
+import com.microcrowd.loader.java3d.max3ds.ChunkChopper;
+
+/**
+ * A KeyFramerInfoChunk stores information about things
+ * that happen to meshes: Position information, rotation
+ * information, scale information, pivot information 
+ * and frame information.
+ * Together with the frames chunk thes are used
+ * display animation behaviors.
+ *
+ * @author Josh DeFord 
+ */
+public class KeyFramerInfoChunk extends Chunk
+{
+
+    /**
+     * Retrieves the named object for the current key framer
+     * inserts the rotation, position and pivot transformations for frame 0
+     * and assigns the coordinate system to it.
+     *
+     * The inverse of the local coordinate system converts from 3ds 
+     * semi-absolute coordinates (what is in the file) to local coordinates.
+     *
+     * Then these local coordinates are converted with matrix 
+     * that will instantiate them to absolute coordinates:
+     * Xabs = sx a1 (Xl-Px) + sy a2 (Yl-Py) + sz a3 (Zl-Pz) + Tx
+     * Yabs = sx b1 (Xl-Px) + sy b2 (Yl-Py) + sz b3 (Zl-Pz) + Ty
+     * Zabs = sx c1 (Xl-Px) + sy c2 (Yl-Py) + sz c3 (Zl-Pz) + Tz
+     * Where:
+     * (Xabs,Yabs,Zabs) = absolute coordinate
+     * (Px,Py,Pz) = mesh pivot (constant)
+     * (X1,Y1,Z1) = local coordinates
+     *
+     * @param chopper the ChunkChopper containing the current state of the parser. 
+     */
+    public void initialize(ChunkChopper chopper) 
+    {
+        String meshName = (String)chopper.getObjectName();
+        Behavior frameBehavior = chopper.getKeyFramer().createBehavior(meshName,
+                                                        chopper.getNamedTransformGroup(meshName),
+                                                        chopper.getNamedObject(meshName));
+        if(frameBehavior != null)
+            chopper.addBehaviorNode(frameBehavior);
+    }
+}
diff --git a/com/microcrowd/loader/java3d/max3ds/chunks/LightChunk.java b/com/microcrowd/loader/java3d/max3ds/chunks/LightChunk.java
new file mode 100644
index 0000000..501697d
--- /dev/null
+++ b/com/microcrowd/loader/java3d/max3ds/chunks/LightChunk.java
@@ -0,0 +1,80 @@
+/**
+ * Make a donation http://sourceforge.net/donate/index.php?group_id=98797
+ * Microcrowd.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * Contact Josh DeFord jdeford@microcrowd.com
+ */
+package com.microcrowd.loader.java3d.max3ds.chunks;
+
+import javax.media.j3d.BoundingSphere;
+import javax.media.j3d.PointLight;
+import javax.media.j3d.Transform3D;
+import javax.media.j3d.TransformGroup;
+import javax.vecmath.Color3f;
+import javax.vecmath.Point3d;
+import javax.vecmath.Vector3f;
+import com.microcrowd.loader.java3d.max3ds.ChunkChopper;
+import com.microcrowd.loader.java3d.max3ds.ChunkMap;
+/**
+ * Lights to be placed in a scene.
+ * Only point lights and target spot lights are supported.
+ * All the default parameters are used for lights as well.
+ * Only position is specified.
+ */
+public class LightChunk extends Chunk
+{
+    private Vector3f currentPosition;
+
+    /**
+     * This is called by the chunk chopper before any of the chunk's 
+     * subchunks  are loaded.  Any data loaded that may need to be 
+     * used later by superchunks should be stored in
+     * the chunk chopper via {@link ChunkChopper#pushData}
+     *
+     * @param chopper used to store the position of the light. 
+     */
+    public void loadData(ChunkChopper chopper)
+    {
+        currentPosition = chopper.getVector();
+        TransformGroup group = chopper.getGroup();
+        Transform3D transform = new Transform3D();
+        group.getTransform(transform);
+        transform.setTranslation(currentPosition);
+        group.setTransform(transform);
+        chopper.pushData(chopper.getID(), currentPosition);
+    }
+
+    /**
+     * Gets the data put into the chopper by the subchunks
+     * and creates a light, adding it to the scene as a named object.
+     * @param chopper the ChunkChopper containing sub chunk data.
+     */
+    public void initialize(ChunkChopper chopper)
+    {
+        Color3f color = (Color3f)chopper.popData(ChunkMap.COLOR);
+        PointLight light = (PointLight)chopper.popData(ChunkMap.SPOTLIGHT);
+        if(light == null)
+        {
+            light = new PointLight();
+            chopper.addLightNode(light);
+        }
+
+        light.setColor(color);
+        light.setInfluencingBounds(new BoundingSphere(new Point3d(0,0,0), 3000));
+        chopper.getGroup().addChild(light);
+    }
+}
diff --git a/com/microcrowd/loader/java3d/max3ds/chunks/MaterialChunk.java b/com/microcrowd/loader/java3d/max3ds/chunks/MaterialChunk.java
new file mode 100644
index 0000000..e22c161
--- /dev/null
+++ b/com/microcrowd/loader/java3d/max3ds/chunks/MaterialChunk.java
@@ -0,0 +1,121 @@
+/**
+ * Make a donation http://sourceforge.net/donate/index.php?group_id=98797
+ * Microcrowd
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * Contact Josh DeFord jdeford@microcrowd.com
+ */
+
+package com.microcrowd.loader.java3d.max3ds.chunks;
+
+import javax.media.j3d.Appearance;
+import javax.media.j3d.Material;
+import javax.media.j3d.PolygonAttributes;
+import javax.media.j3d.Texture;
+import javax.media.j3d.TransparencyAttributes;
+import javax.vecmath.Color3f;
+import com.microcrowd.loader.java3d.max3ds.ChunkChopper;
+import com.microcrowd.loader.java3d.max3ds.ChunkMap;
+
+
+
+/**
+ * Loads material chunks with ambient, diffuse and specular colors,
+ * shininess, transparency, two sidedness and texture.
+ */
+public class MaterialChunk extends Chunk
+{
+
+    //public static final Integer SELF_ILLUMINATED = new Integer((short)0xA084);
+
+
+    /**
+     * This will set the ambient, diffuse and specular
+     * colors as well as the textures, two sidedness
+     * and transparency of the material.
+     *
+     * @param chopper the chopper containing the data
+     * needed to set the attributes.
+     */
+    public void initialize(ChunkChopper chopper)
+    {
+        Appearance appearance = new Appearance();
+        Material material = new Material();
+
+        Color3f ambientColor = (Color3f)chopper.popData(ChunkMap.AMBIENT_COLOR);
+        if (ambientColor != null) {
+            material.setAmbientColor(ambientColor);
+        }
+
+        Color3f color = (Color3f)chopper.popData(ChunkMap.DIFFUSE_COLOR);
+        if (color != null) {
+            material.setDiffuseColor(color);
+        }
+
+        color = (Color3f)chopper.popData(ChunkMap.SPECULAR_COLOR);
+        if (color != null) {
+            material.setSpecularColor(color);
+        }
+
+        Texture texture = (Texture)chopper.popData(ChunkMap.TEXTURE);
+        if(texture != null)
+        {
+            appearance.setTexture(texture);
+        }
+
+        Boolean twoSided = (Boolean)chopper.popData(ChunkMap.TWO_SIDED);
+        if (twoSided != null) //Just being there is equivalent to a boolean true.
+        {
+
+            PolygonAttributes polyAttributes = appearance.getPolygonAttributes(); 
+            if(polyAttributes == null)
+            {
+                polyAttributes = new PolygonAttributes();
+            }
+
+            polyAttributes.setCullFace(PolygonAttributes.CULL_NONE);
+            appearance.setPolygonAttributes(polyAttributes);
+        }
+
+        Float transparency = (Float)chopper.popData(ChunkMap.TRANSPARENCY);
+        if (transparency != null) {
+            if (transparency.floatValue() > 0.01f) {
+
+                TransparencyAttributes transparencyAttributes = new TransparencyAttributes(TransparencyAttributes.FASTEST, transparency.floatValue());
+                appearance.setTransparencyAttributes(transparencyAttributes);
+            }
+        }
+
+        String name = (String)chopper.popData(ChunkMap.MATERIAL_NAME);
+        Float shininess = (Float)chopper.popData(ChunkMap.SHININESS);
+        if (shininess != null) 
+        {
+            float shine = shininess.floatValue() * 1024f;
+            material.setShininess(shine);
+        }
+
+        /*
+           Boolean illuminated = (Boolean)chopper.popData(SELF_ILLUMINATED);
+           if(illuminated != null && illuminated.booleanValue() == true)
+           {
+           material.setEmissiveColor(ambientColor);
+           }
+           */
+
+        appearance.setMaterial(material);
+        chopper.setNamedObject(name, appearance);
+    }
+}
diff --git a/com/microcrowd/loader/java3d/max3ds/chunks/NamedObjectChunk.java b/com/microcrowd/loader/java3d/max3ds/chunks/NamedObjectChunk.java
new file mode 100644
index 0000000..ee063a9
--- /dev/null
+++ b/com/microcrowd/loader/java3d/max3ds/chunks/NamedObjectChunk.java
@@ -0,0 +1,50 @@
+/**
+ * Make a donation http://sourceforge.net/donate/index.php?group_id=98797
+ * Microcrowd.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * Contact Josh DeFord jdeford@microcrowd.com
+ */
+
+package com.microcrowd.loader.java3d.max3ds.chunks;
+
+import javax.media.j3d.TransformGroup;
+import com.microcrowd.loader.java3d.max3ds.ChunkChopper;
+
+/**
+ * Loads information about a named object: Cameras, meshes and lights
+ */
+public class NamedObjectChunk extends Chunk
+{
+
+    /**
+     * Adds a TransformGroup the the chopper's branch
+     * group to which meshes will be added.
+     *
+     * @param chopper The chopper containing the state of parsing.  
+     */
+    public void loadData(ChunkChopper chopper)
+    {
+        final String name = chopper.getString();
+        TransformGroup transformGroup = new TransformGroup();
+
+        transformGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
+        transformGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
+        transformGroup.setCapability(TransformGroup.ENABLE_PICK_REPORTING);
+
+        chopper.addObject(name, transformGroup);
+    }
+}
diff --git a/com/microcrowd/loader/java3d/max3ds/chunks/PercentageChunk.java b/com/microcrowd/loader/java3d/max3ds/chunks/PercentageChunk.java
new file mode 100644
index 0000000..9036aff
--- /dev/null
+++ b/com/microcrowd/loader/java3d/max3ds/chunks/PercentageChunk.java
@@ -0,0 +1,59 @@
+/**
+ * Make a donation http://sourceforge.net/donate/index.php?group_id=98797
+ * Microcrowd.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * Contact Josh DeFord jdeford@microcrowd.com
+ */
+
+package com.microcrowd.loader.java3d.max3ds.chunks;
+
+import com.microcrowd.loader.java3d.max3ds.ChunkChopper;
+
+/**
+ * Loads percentage values from binary data representing them.
+ */
+public class PercentageChunk extends Chunk
+{
+    /** Represents an int percentage */
+    public static final int INT = 0x30;
+
+    /** Represents a float percentage */
+    public static final int FLOAT = 0x31;
+    private float percentage;
+
+    /**
+     * Gets tye type of percentage, reads it
+     * and sets the value on the chopper using
+     * the id of the current chunk as the key.
+     *
+     */
+    public void loadData(ChunkChopper chopper)
+    {
+        int percentageType = chopper.getUnsignedShort();
+        int percentageLength = chopper.getUnsignedInt();
+        if (percentageType == INT) {
+            percentage = (chopper.getUnsignedShort()) / 100f;
+            chopper.pushData(chopper.getID(), new Float(percentage));
+        } else if (percentageType == FLOAT) {
+            percentage = chopper.getFloat() / 100f;
+            chopper.pushData(chopper.getID(), new Float(percentage));
+        } else {
+            throw new IllegalArgumentException("Only float and int percentages are enabled.");
+        }
+
+    }
+}
diff --git a/com/microcrowd/loader/java3d/max3ds/chunks/PivotChunk.java b/com/microcrowd/loader/java3d/max3ds/chunks/PivotChunk.java
new file mode 100644
index 0000000..f9631b1
--- /dev/null
+++ b/com/microcrowd/loader/java3d/max3ds/chunks/PivotChunk.java
@@ -0,0 +1,45 @@
+/**
+ * Make a donation http://sourceforge.net/donate/index.php?group_id=98797
+ * Microcrowd.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * Contact Josh DeFord jdeford@microcrowd.com
+ */
+
+package com.microcrowd.loader.java3d.max3ds.chunks;
+
+import javax.vecmath.Point3f;
+import javax.vecmath.Vector3f;
+import com.microcrowd.loader.java3d.max3ds.ChunkChopper;
+/**
+ * Loads the pivot for a mesh. 
+ * {@see KeyFramerInfoChunk} for more information about using
+ * animations from a 3ds file
+ */
+public class PivotChunk extends Chunk
+{
+    /**
+     * Gets the pivot and associates it with the current mes.
+     * @param chopper the ChunkChopper containing the state of the parser.  
+     */
+    public void loadData(ChunkChopper chopper)
+    {
+        Vector3f pivot = new Vector3f((Point3f)chopper.getPoint());
+
+        chopper.getKeyFramer().setPivot(pivot);
+    }
+
+}
diff --git a/com/microcrowd/loader/java3d/max3ds/chunks/PositionChunk.java b/com/microcrowd/loader/java3d/max3ds/chunks/PositionChunk.java
new file mode 100644
index 0000000..52eea84
--- /dev/null
+++ b/com/microcrowd/loader/java3d/max3ds/chunks/PositionChunk.java
@@ -0,0 +1,63 @@
+/**
+ * Make a donation http://sourceforge.net/donate/index.php?group_id=98797
+ * Microcrowd.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * Contact Josh DeFord jdeford@microcrowd.com
+ */
+
+package com.microcrowd.loader.java3d.max3ds.chunks;
+
+import java.util.ArrayList;
+import javax.vecmath.Point3f;
+import com.microcrowd.loader.java3d.max3ds.ChunkChopper;
+
+/**
+ * Loads the position of a mesh as defined in the 3ds file.
+ * This position may need to be converted to another coordinate
+ * system by KeyFramerInfoChunk.
+ *  * {@see KeyFramerInfoChunk} for more information about using
+ * animations from a 3ds file
+ */
+public class PositionChunk extends Chunk
+{
+    /**
+     * Loads the position for a shape and KeyFramerInfoChunk
+     *
+     * @param chopper the ChunkChopper containing the state of the parser.  
+     */
+    public void loadData(ChunkChopper chopper)
+    {
+        int flags = chopper.getUnsignedShort();
+        chopper.getLong();
+        int numKeys = chopper.getUnsignedInt();
+
+        ArrayList pointList = new ArrayList();
+        for(int i =0; i < numKeys; i++)
+        {
+            long keyNumber = chopper.getUnsignedInt();
+            int  accelerationData = chopper.getUnsignedShort(); 
+
+            Point3f position = chopper.getPoint(); 
+            if(i==0)
+            {
+                chopper.getKeyFramer().setPosition(position);
+            }
+            pointList.add(position);
+        }
+        chopper.getKeyFramer().setPositionKeys(pointList);
+    }
+}
diff --git a/com/microcrowd/loader/java3d/max3ds/chunks/RotationChunk.java b/com/microcrowd/loader/java3d/max3ds/chunks/RotationChunk.java
new file mode 100644
index 0000000..8f4f2c0
--- /dev/null
+++ b/com/microcrowd/loader/java3d/max3ds/chunks/RotationChunk.java
@@ -0,0 +1,129 @@
+/*8
+ * Make a donation http://sourceforge.net/donate/index.php?group_id=98797
+ * Microcrowd.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * Contact Josh DeFord jdeford@microcrowd.com
+ */
+
+package com.microcrowd.loader.java3d.max3ds.chunks;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.vecmath.Quat4f;
+import javax.vecmath.Vector3f;
+import com.microcrowd.loader.java3d.max3ds.ChunkChopper;
+
+/**
+ * Extracts the rotation information from the 3ds file.
+ * Rotations occur about a pivot at a position within
+ * a local coordinate system. The rotation information
+ * is provided to the KeyFramerInfoChunk which then converts
+ * it to a global coordinate system and applies animation
+ * information.
+ * {@see KeyFramerInfoChunk} for more information about using
+ * animations from a 3ds file
+ */
+public class RotationChunk extends Chunk
+{
+    /** 
+     *  String that will be used as a data object in the transform that the 
+     *  RotationInterpolator will be a child of so it may be look up later.
+     **/
+    public static String ROTATION_TAG = "ROTATION_INTERPOLATOR";
+
+    /**
+     * Loads the quaternion for a rotation of a shape
+     * and notifies mesh info chunk.
+     *
+     * @param chopper the ChunkChopper containing the state of the parser.  
+     */
+    public void loadData(ChunkChopper chopper)
+    {
+        int flags = chopper.getUnsignedShort();
+        chopper.getLong();
+        int numKeys = chopper.getUnsignedInt();
+
+        Quat4f previousQuat = null;
+
+        List quats = new ArrayList();
+        for(int i =0; i < numKeys; i++)
+        {
+            long         frameNumber = chopper.getUnsignedInt();//Part of the track header
+            int   accelerationData = chopper.getUnsignedShort();//Part of the track header
+            getSplineTerms(accelerationData, chopper);//Part of the track header
+
+            float            angle = chopper.getFloat();
+            Vector3f        vector = chopper.getVector(); 
+
+            Quat4f quat = getQuaternion(vector, angle);
+            if(previousQuat != null) {
+                quat.mul(previousQuat, quat);
+            }
+            previousQuat = quat; 
+
+            quats.add(quat); 
+            if(i==0)
+            {
+                chopper.getKeyFramer().setRotation(quat);
+            }
+        }
+        chopper.getKeyFramer().setOrientationKeys(quats);
+    }
+
+    /**
+     * This only reads the spline data and should be part
+     * of the track header when it gets invented.
+     * @param chopper an integer representing the bits that 
+     * determine which of the five possible spline terms are present in the 
+     * data and should be read.
+     * @param chopper what to read the data from
+     * The possible spline values are are 
+     * <ol>
+     * <li> Tension
+     * <li> Continuity
+     * <li> Bias
+     * <li> EaseTo
+     * <li> EaseFrom
+     * </ol>
+     */
+    private void getSplineTerms(final int accelerationData, ChunkChopper chopper)
+    {
+        int bits = accelerationData;
+        for(int i=0; i < 5; i++)
+        {
+            bits = bits >>> i;
+            if((bits & 1) == 1)
+            {
+                chopper.getFloat();
+            }
+        }
+    }
+
+    /**
+     * This converts a 3ds angle and axis to 
+     * a quaternion rotation.  Successive
+     * rotations are relative to the first so each
+     * rotation must be made absolute by multiplying
+     * it with its predecessor
+     */
+    public Quat4f getQuaternion(Vector3f axis, float angle)
+    {
+        float sinA  = (float)(java.lang.Math.sin(angle/2.0f));
+        float cosA  = (float)(java.lang.Math.cos(angle/2.0f));
+        return new Quat4f(axis.x * sinA, axis.y * sinA, axis.z * sinA, cosA);
+    }
+}
diff --git a/com/microcrowd/loader/java3d/max3ds/chunks/ScaleChunk.java b/com/microcrowd/loader/java3d/max3ds/chunks/ScaleChunk.java
new file mode 100644
index 0000000..30fb564
--- /dev/null
+++ b/com/microcrowd/loader/java3d/max3ds/chunks/ScaleChunk.java
@@ -0,0 +1,67 @@
+/**
+ * Make a donation http://sourceforge.net/donate/index.php?group_id=98797
+ * Microcrowd.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * Contact Josh DeFord jdeford@microcrowd.com
+ */
+
+package com.microcrowd.loader.java3d.max3ds.chunks;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.vecmath.Vector3f;
+import com.microcrowd.loader.java3d.max3ds.ChunkChopper;
+
+/**
+ * Extracts scale information from the 3ds file which
+ * is then used by the mesh info chunk to construct a 
+ * animation.
+ */
+public class ScaleChunk extends Chunk
+{
+    /**
+     * Loads the scale for a shape
+     * and notifies the KeyFramerInfoChunk
+     *
+     * @param chopper the ChunkChopper containing the state of the parser.  
+     */
+    public void loadData(ChunkChopper chopper)
+    {
+        int flags = chopper.getUnsignedShort();
+        chopper.getLong();
+        int numKeys = chopper.getUnsignedInt();
+
+        List scaleKeys = new ArrayList();
+
+        for(int i =0; i < numKeys; i++)
+        {
+            long keyNumber = chopper.getUnsignedInt();
+            int  accelerationData = chopper.getUnsignedShort(); 
+
+            float scaleX = chopper.getFloat();
+            float scaleZ = chopper.getFloat();
+            float scaleY = chopper.getFloat();
+            Vector3f scale = new Vector3f(scaleX, scaleY, scaleZ);
+            if(i==0)
+            {
+                chopper.getKeyFramer().setScale(scale);
+            }
+            scaleKeys.add(scale);
+        }
+        chopper.getKeyFramer().setScaleKeys(scaleKeys);
+    }
+}
diff --git a/com/microcrowd/loader/java3d/max3ds/chunks/SmoothingChunk.java b/com/microcrowd/loader/java3d/max3ds/chunks/SmoothingChunk.java
new file mode 100644
index 0000000..ae92155
--- /dev/null
+++ b/com/microcrowd/loader/java3d/max3ds/chunks/SmoothingChunk.java
@@ -0,0 +1,65 @@
+/**
+ * Make a donation http://sourceforge.net/donate/index.php?group_id=98797
+ * Microcrowd.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * Contact Josh DeFord jdeford@microcrowd.com
+ */
+package com.microcrowd.loader.java3d.max3ds.chunks;
+
+import javax.vecmath.Point3f;
+import com.microcrowd.loader.java3d.max3ds.ChunkChopper;
+import com.microcrowd.loader.java3d.max3ds.ChunkMap;
+
+/**
+ * This chunk is used to generate normals for a mesh according
+ * to the data in the smoothing groups chunk.
+ * Vertices that share coordinates will all use the same, averaged
+ * normal if they also belong to the same smoothing groups.
+ * @author jdeford
+ */
+public class SmoothingChunk extends Chunk
+{
+    /**
+     * Loads the vertices smoothing groups for 
+     * a mesh and stores it in chopper
+     *
+     * @param chopper the ChunkChopper containing the state of the parser.  
+     */
+    public void loadData(ChunkChopper chopper)
+    {
+        Point3f[] vertices = (Point3f[])chopper.popData(ChunkMap.FACES_DESCRIPTION);
+        int[] smoothGroups = new int[vertices.length];
+        int numFaces = vertices.length/3;
+        for(int i=0; i < numFaces; i++)
+        {
+            int groupMask = chopper.getInt();
+            smoothGroups[i*3]=groupMask;
+            smoothGroups[(i*3)+1]=groupMask;
+            smoothGroups[(i*3)+2]=groupMask;
+
+            java.util.ArrayList list = new java.util.ArrayList();
+            for(int j=0; j < 32; j++)
+            {
+                if(((0x1l << j) & groupMask) > 0)
+                {
+                    list.add(new Integer(j));
+                }
+            }
+        }
+        chopper.pushData(chopper.getID(), smoothGroups);
+    }
+}
diff --git a/com/microcrowd/loader/java3d/max3ds/chunks/SpotLightChunk.java b/com/microcrowd/loader/java3d/max3ds/chunks/SpotLightChunk.java
new file mode 100644
index 0000000..875da30
--- /dev/null
+++ b/com/microcrowd/loader/java3d/max3ds/chunks/SpotLightChunk.java
@@ -0,0 +1,73 @@
+/**
+ * Make a donation http://sourceforge.net/donate/index.php?group_id=98797
+ * Microcrowd.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * Contact Josh DeFord jdeford@microcrowd.com
+ */
+package com.microcrowd.loader.java3d.max3ds.chunks;
+
+import javax.media.j3d.SpotLight;
+import javax.media.j3d.Transform3D;
+import javax.media.j3d.TransformGroup;
+import javax.vecmath.Point3d;
+import javax.vecmath.Point3f;
+import javax.vecmath.Vector3d;
+import javax.vecmath.Vector3f;
+import com.microcrowd.loader.java3d.max3ds.ChunkChopper;
+import com.microcrowd.loader.java3d.max3ds.ChunkMap;
+
+/**
+ * SpotLights to be placed in a scene.
+ *
+ * All the default parameters other than 
+ * position and direction are used and
+ * not loaded from the 3ds file.
+ */
+public class SpotLightChunk extends Chunk
+{
+
+
+    /**
+     * This is called by the chunk chopper before any of the chunk's 
+     * subchunks  are loaded.  Any data loaded that may need to be 
+     * used later by superchunks should be stored in
+     * the chunk chopper via {@link ChunkChopper#popData}
+     *
+     * @param chopper the ChunkChopper that will have the light placed in it.  
+     */
+    public void loadData(ChunkChopper chopper)
+    {
+        Point3f target = chopper.getPoint();
+        float beam = chopper.getFloat();
+        float falloff = chopper.getFloat();
+        SpotLight light = new SpotLight();
+
+        Vector3f direction = new Vector3f(0,0,-1); 
+
+        Vector3f position = (Vector3f)chopper.popData(ChunkMap.LIGHT);
+        TransformGroup group = chopper.getGroup();
+        Transform3D transform = new Transform3D();
+        group.getTransform(transform);
+        transform.lookAt(new Point3d(position), new Point3d(target), new Vector3d(0,1,0));
+        transform.invert();
+        transform.setTranslation(position);
+        group.setTransform(transform);
+
+        chopper.pushData(chopper.getID(), light);
+        chopper.addLightNode(light);
+    }
+}
diff --git a/com/microcrowd/loader/java3d/max3ds/chunks/StringChunk.java b/com/microcrowd/loader/java3d/max3ds/chunks/StringChunk.java
new file mode 100644
index 0000000..b9eb55e
--- /dev/null
+++ b/com/microcrowd/loader/java3d/max3ds/chunks/StringChunk.java
@@ -0,0 +1,52 @@
+/**
+ * Make a donation http://sourceforge.net/donate/index.php?group_id=98797
+ * Microcrowd.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * Contact Josh DeFord jdeford@microcrowd.com
+ */
+
+package com.microcrowd.loader.java3d.max3ds.chunks;
+
+import com.microcrowd.loader.java3d.max3ds.ChunkChopper;
+
+/**
+ * These have no subchunks. Only String data terminated with a null. For
+ * strings with unknown length use {@link ChunkChopper#getString} This can
+ * also be used for chunks that have data of a known length beginning with a
+ * string with unnessecary(you don't want to use it) data following.
+ */
+public class StringChunk extends Chunk
+{
+    /**
+     * Reads in all the data for this chunk and makes a string out of it.
+     * This will set the data in the chopper with a key of this chunks id.
+     *
+     *
+     * @param chopper the chopper that is doing the parsing.  
+     */
+    public void loadData(ChunkChopper chopper)
+    {
+        byte[] stringArray = chopper.getChunkBytes();
+
+        String value = new String(stringArray, 0, stringArray.length - 1);
+        if (value.indexOf((char)(0x0000)) > 0) {
+            value = value.substring(0, value.indexOf((char)(0x0000)));
+        }
+
+        chopper.pushData(chopper.getID(), value);
+    }
+}
diff --git a/com/microcrowd/loader/java3d/max3ds/chunks/TextureChunk.java b/com/microcrowd/loader/java3d/max3ds/chunks/TextureChunk.java
new file mode 100644
index 0000000..ebbb655
--- /dev/null
+++ b/com/microcrowd/loader/java3d/max3ds/chunks/TextureChunk.java
@@ -0,0 +1,47 @@
+/**
+ * Make a donation http://sourceforge.net/donate/index.php?group_id=98797
+ * Microcrowd.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * Contact Josh DeFord jdeford@microcrowd.com
+ */
+
+package com.microcrowd.loader.java3d.max3ds.chunks;
+
+
+import com.microcrowd.loader.java3d.max3ds.ChunkChopper;
+import com.microcrowd.loader.java3d.max3ds.ChunkMap;
+
+
+/**
+ * Loads percentage values from binary data representing them.
+ */
+public class TextureChunk extends Chunk
+{
+
+    /**
+     * Gets the current texture image from the chopper
+     * creates a texture with it and sets that texture
+     * on the chopper.
+     *
+     * @param chopper  the parser containing the state of parsing
+     */
+    public void initialize(ChunkChopper chopper)
+    {
+        String textureName = (String)chopper.popData(ChunkMap.TEXTURE_NAME);
+        chopper.pushData(ChunkMap.TEXTURE, chopper.createTexture(textureName));
+    }
+}
diff --git a/com/microcrowd/loader/java3d/max3ds/chunks/Vertex2ListChunk.java b/com/microcrowd/loader/java3d/max3ds/chunks/Vertex2ListChunk.java
new file mode 100644
index 0000000..7a8ed15
--- /dev/null
+++ b/com/microcrowd/loader/java3d/max3ds/chunks/Vertex2ListChunk.java
@@ -0,0 +1,55 @@
+/**
+ * Make a donation http://sourceforge.net/donate/index.php?group_id=98797
+ * Microcrowd.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * Contact Josh DeFord jdeford@microcrowd.com
+ */
+
+package com.microcrowd.loader.java3d.max3ds.chunks;
+
+import javax.vecmath.TexCoord2f;
+import com.microcrowd.loader.java3d.max3ds.ChunkChopper;
+
+/**
+ * Reads a list of x,y points that will be used
+ * later for texture mapping.
+ *
+ * @author jdeford 
+ */
+public class Vertex2ListChunk extends Chunk
+{
+    private static final int POINT_2F_SIZE = 8;
+
+    /**
+     * Reads all the point data from the chopper and stores
+     * teh points in the chopper.
+     *
+     * @param chopper the chopper that will parse and store
+     * the data using this chunks id as the key. 
+     */
+    public void loadData(ChunkChopper chopper)
+    {
+        int numVertices = chopper.getUnsignedShort();
+        TexCoord2f[] points = new TexCoord2f[numVertices];
+        for (int i = 0; i < numVertices; i++) {
+            float point0 = chopper.getFloat();
+            float point1 = chopper.getFloat();
+            points[i] = new TexCoord2f(point0, point1);
+        }
+        chopper.pushData(chopper.getID(), points);
+    }
+}
diff --git a/com/microcrowd/loader/java3d/max3ds/chunks/Vertex3ListChunk.java b/com/microcrowd/loader/java3d/max3ds/chunks/Vertex3ListChunk.java
new file mode 100644
index 0000000..be5ae4e
--- /dev/null
+++ b/com/microcrowd/loader/java3d/max3ds/chunks/Vertex3ListChunk.java
@@ -0,0 +1,54 @@
+/**
+ * Make a donation http://sourceforge.net/donate/index.php?group_id=98797
+ * Microcrowd.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * Contact Josh DeFord jdeford@microcrowd.com
+ */
+
+package com.microcrowd.loader.java3d.max3ds.chunks;
+
+import javax.vecmath.Point3f;
+import com.microcrowd.loader.java3d.max3ds.ChunkChopper;
+
+/**
+ * Reads and store x,y,z vertex coordinates.
+ * The coordinates will be accessed with indexes to construct 
+ * the mesh out of triangles.
+ * @author  jdeford
+ */
+public class Vertex3ListChunk extends Chunk
+{
+    private static final int POINT_3F_SIZE = 12;
+
+    /**
+     * Reads all the point data from the chopper
+     * and stores it using this chunk's id as the key.
+     *
+     * @param chopper the chopper that will read and
+     * store the data. 
+     */
+    public void loadData(ChunkChopper chopper)
+    {
+        int numVertices = chopper.getUnsignedShort();
+        Point3f[] points = new Point3f[numVertices];
+        for (int i = 0; i < numVertices; i++) {
+            points[i] = new Point3f(chopper.getPoint());
+        }
+
+        chopper.pushData(chopper.getID(), points);
+    }
+}
diff --git a/com/microcrowd/loader/java3d/max3ds/data/KeyFramer.java b/com/microcrowd/loader/java3d/max3ds/data/KeyFramer.java
new file mode 100644
index 0000000..d98d830
--- /dev/null
+++ b/com/microcrowd/loader/java3d/max3ds/data/KeyFramer.java
@@ -0,0 +1,526 @@
+/**
+ * Make a donation http://sourceforge.net/donate/index.php?group_id=98797
+ *
+ * Microcrowd.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * Contact Josh DeFord jdeford@microcrowd.com
+ */
+package com.microcrowd.loader.java3d.max3ds.data;
+
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import javax.media.j3d.Alpha;
+import javax.media.j3d.Behavior;
+import javax.media.j3d.Group;
+import javax.media.j3d.Node;
+import javax.media.j3d.RotPosPathInterpolator;
+import javax.media.j3d.Transform3D;
+import javax.media.j3d.TransformGroup;
+import javax.media.j3d.TransformInterpolator;
+import javax.vecmath.Matrix4f;
+import javax.vecmath.Point3f;
+import javax.vecmath.Quat4f;
+import javax.vecmath.Vector3d;
+import javax.vecmath.Vector3f;
+
+/**
+ * @author Josh DeFord 
+ */
+public class KeyFramer
+{
+    private HashMap lastGroupMap = new HashMap();
+    private HashMap fatherMap = new HashMap();
+
+    private Quat4f   rotation;
+    private Point3f  position;
+    private Point3f  pivotCenter;
+    private Vector3f pivot;
+    private Vector3f scale;
+    private HashMap  namedObjectCoordinateSystems = new HashMap();
+
+    private List positionKeys;
+    private List orientationKeys;
+    private List scaleKeys;
+
+    private Integer id;
+    private Group father;
+    private Group dummyObject;
+
+
+    /**
+     * Retrieves the named object for the current key framer
+     * inserts the rotation, position and pivot transformations for frame 0
+     * and assigns the coordinate system to it.
+     *
+     * The inverse of the local coordinate system converts from 3ds 
+     * semi-absolute coordinates (what is in the file) to local coordinates.
+     *
+     * Then these local coordinates are converted with matrix 
+     * that will instantiate them to absolute coordinates:
+     * Xabs = sx a1 (Xl-Px) + sy a2 (Yl-Py) + sz a3 (Zl-Pz) + Tx
+     * Yabs = sx b1 (Xl-Px) + sy b2 (Yl-Py) + sz b3 (Zl-Pz) + Ty
+     * Zabs = sx c1 (Xl-Px) + sy c2 (Yl-Py) + sz c3 (Zl-Pz) + Tz
+     * Where:
+     * (Xabs,Yabs,Zabs) = absolute coordinate
+     * (Px,Py,Pz) = mesh pivot (constant)
+     * (X1,Y1,Z1) = local coordinates
+     *
+     */
+    public Behavior createBehavior(String meshName, Group transformGroup, Object testObject)
+    {
+        Group objectGroup = getObjectByName(meshName, transformGroup, testObject);
+        //System.out.println("mesh " + meshName + " scale " + scale);
+        if(objectGroup == null)
+            return null;
+
+        insertFather(objectGroup, meshName);
+
+        TransformInterpolator behavior = null;
+        Transform3D coordinateSystem  = (Transform3D)namedObjectCoordinateSystems.get(meshName);
+
+        //Gonna put these children back later.
+        Enumeration children = removeChildren(objectGroup);
+
+        Transform3D coordinateTransform = coordinateSystem == null ? new Transform3D() : new Transform3D(coordinateSystem);
+
+        Transform3D targetTransform = new Transform3D();
+        TransformGroup targetGroup = new TransformGroup(targetTransform);
+
+        TransformGroup localCoordinates = hasKeys() ? buildLocalCoordinates(coordinateSystem) : new TransformGroup();
+        TransformGroup lastGroup = (TransformGroup)addGroups(objectGroup, new Group[]
+                {
+                    localCoordinates,
+                    targetGroup,
+                    buildPivotGroup(coordinateTransform, pivot),
+                    buildKeysGroup(),
+                });
+
+        addChildren(children, lastGroup);
+        lastGroupMap.put(objectGroup, lastGroup);
+
+
+        behavior = buildInterpolator(targetGroup, coordinateSystem);
+        if(behavior != null)
+        {
+            behavior.setEnable(false);
+            targetGroup.addChild(behavior);
+
+            behavior.computeTransform(0f, targetTransform);
+            targetGroup.setTransform(targetTransform);
+        }
+        return behavior;
+    }
+
+    private Enumeration removeChildren(Group group)
+    {
+        Enumeration children = group.getAllChildren(); 
+        group.removeAllChildren();
+        return children;
+    }
+
+    private void addChildren(Enumeration children, Group group)
+    {
+        if(group == null)
+            return;
+        while(children.hasMoreElements())
+        {
+            Node node = (Node)(children.nextElement());
+            group.addChild(node);
+        }
+    }
+
+    /**
+     * Looks up the current object.
+     * objectGroup is returned if it is the right one to return
+     * otherwise a new dummy object may be returned.
+     * If it isn't there it gets the dummy object
+     * from the frames description chunk.
+     */
+    private Group getObjectByName(String objectName, Group objectGroup, Object testObject)
+    {
+
+        //This means its a dummy object.  It needs to be created.
+        if(objectGroup == null && testObject == null)
+        {
+            namedObjectCoordinateSystems.put(objectName, new Transform3D());
+            objectGroup = dummyObject;
+        }
+
+        return objectGroup;
+    }
+
+    /**
+     * Locates the father for the object named objectName and inserts 
+     * its transform cluster between the parent and all
+     * the parent's children. This only occurs in a hierarchical
+     * model. like bones and stuff. The fatherGroup, if found,
+     * is removed from whatever parent belonged to on before insertion.
+     */
+    private void insertFather(Group parentGroup, String objectName)
+    {
+        if(father == null)
+            return;
+        Group topGroup = new TransformGroup(); 
+        topGroup.addChild(father);
+        Group bottomGroup = (Group)lastGroupMap.get(father);
+
+        if(topGroup == null)
+            return;
+
+        Group fatherParent = (Group)topGroup.getParent();
+        if(fatherParent != null)
+            fatherParent.removeChild(topGroup);
+        
+        Enumeration originalChildren = removeChildren(parentGroup);
+        parentGroup.addChild(topGroup);
+        addChildren(originalChildren, bottomGroup);
+    }
+
+    /**
+     * Builds a transform group from the zeroth key of the 
+     * position and rotation tracks.
+     * @return transform group with position and rotation information
+     */
+    private TransformGroup buildKeysGroup()
+    {
+        Transform3D positionTransform   = new Transform3D();
+        positionTransform.set(new Vector3f(position));
+
+        Transform3D rotationTransform   = new Transform3D();
+        rotationTransform.set(rotation);
+
+        Transform3D scaleTransform = new Transform3D();
+        scaleTransform.setScale(new Vector3d(scale));
+        TransformGroup scaleGroup = new TransformGroup(scaleTransform);
+
+        Transform3D keyTransform = new Transform3D(positionTransform);
+        keyTransform.mul(scaleTransform);
+        keyTransform.mul(rotationTransform);
+        return new TransformGroup(keyTransform);
+    }
+
+    /**
+     * Builds a pivot group that will allow the objects
+     * to be positioned properly according to their rotations
+     * and positions.
+     * @param coordinateTransform the coordinate system defining the 
+     * location and orientation of the local axis. This is not modified.
+     * @param pivot the pivot defined in the 3ds file loaded by pivot chunk.
+     * This is not changed.
+     */
+    private TransformGroup buildPivotGroup(Transform3D coordinateTransform, Vector3f pivot)
+    {
+            Transform3D pivotTransform = new Transform3D();
+            pivotTransform.mulInverse(coordinateTransform);
+            pivot = new Vector3f(pivot);
+            pivot.negate();
+            translatePivot(pivotTransform, pivot, pivotCenter);
+            return new TransformGroup(pivotTransform);
+    }
+
+    /**
+     * Builds a coordinate group that will allow the objects
+     * to be positioned properly according to their rotations
+     * and positions.
+     * @param coordinateSystem the coordinate system defining the 
+     * location and orientation of the local axis. This is modified 
+     * so it will be useful during the construction
+     * of the animations.
+     */
+    private TransformGroup buildLocalCoordinates(Transform3D coordinateSystem)
+    {
+        Matrix4f coordMatrix = new Matrix4f();
+        Vector3f translation = new Vector3f();
+
+        coordinateSystem.get(translation);
+        coordinateSystem.invert();
+
+        coordinateSystem.get(coordMatrix);
+        coordMatrix.m03 = translation.x;
+        coordMatrix.m13 = translation.y;
+        coordMatrix.m23 = translation.z;
+        coordinateSystem.set(coordMatrix);
+        coordinateSystem.invert();
+        TransformGroup systemGroup = new TransformGroup(coordinateSystem);
+        coordinateSystem.invert();
+        return systemGroup;
+    }
+
+   /**
+     * Hierarchically adds the provided groups in order to parentGroup.
+     * groups[0] is added to parentGroup, groups[1] is added to groups[0] etc.
+     * @return the last group added (groups[groups.length - 1]).
+     */
+    private Group addGroups(Group parentGroup, Group[] groups)
+    {
+        Group nextGroup = parentGroup;
+        for(int i=0; i < groups.length; i++)
+        {
+            nextGroup.addChild(groups[i]);
+            nextGroup = groups[i];
+        }
+        return groups[groups.length - 1];
+    }
+
+    /**
+     * Does a pre rotational translation of the pivot.
+     * @param transform the matrix that will have a translation concatenated to it.
+     * @param vector the vector which will be used to translate the matrix.
+     * @param offset the offset used to offset the pivot. 
+     */
+    private void translatePivot(Transform3D transform, Vector3f vector, Point3f offset)
+    {
+        if(offset != null)
+        {
+            pivot.sub(offset);
+        }
+        Matrix4f matrix = new Matrix4f();
+        transform.get(matrix);
+
+        matrix.m03 += (matrix.m00*vector.x + matrix.m01*vector.y + matrix.m02*vector.z);
+        matrix.m13 += (matrix.m10*vector.x + matrix.m11*vector.y + matrix.m12*vector.z);
+        matrix.m23 += (matrix.m20*vector.x + matrix.m21*vector.y + matrix.m22*vector.z);
+
+        transform.set(matrix);
+    }
+
+
+    /**
+     * Builds a rotation position interpolator for use on this mesh using position and rotation information
+     * adds it to targetGroup.
+     * This does not set the capability bits that need to be set for the animation  
+     * to be used. The capability bits of the targetGroup must be set by the client application.
+     * The alpha object on the Interpolator must also be enabled. 
+     * The Interpolator must also have its scheduling bounds set.
+     * @param pivotGroup transform group which will be operated on by the interpolator.
+     * @param interpolatorAxis the axis that about which rotations will be centered.
+     */
+    //TODO... This needs to use both a rotation interpolator and a position interpolator
+    //in case there are keys with no position information but position information and 
+    //vice versa right now its using RotPosPathInterpolator
+    private TransformInterpolator buildInterpolator(TransformGroup targetGroup, Transform3D axisOfTransform)
+    {
+        makeTwoListsTheSameSize(positionKeys, orientationKeys);
+        int numKeys = positionKeys.size();
+
+        Point3f currentPoint = position; 
+        Quat4f  currentQuat  = rotation; 
+        RotPosPathInterpolator rotator = null; 
+        if(numKeys > 1) 
+        {
+            float[]    knots = new float[numKeys];
+            Point3f[] points = new Point3f[numKeys];
+            Quat4f[]  quats  = new Quat4f[numKeys];
+
+            for(int i=0; i < numKeys; i++) 
+            {
+                //Knots need to be between 0(beginning) and 1(end)
+                knots[i]= (i==0?0:((float)i/((float)(numKeys-1))));
+                if(positionKeys.size() > i)
+                {
+                    Point3f newPoint = (Point3f)positionKeys.get(i);
+                    if(newPoint != null)
+                    {
+                        currentPoint = newPoint;
+                    }
+
+                    Quat4f newQuat = (Quat4f)orientationKeys.get(i);
+                    if(newQuat != null)
+                    {
+                        currentQuat = newQuat;
+                    }
+                }
+
+                points[i] = currentPoint;
+                quats[i] = currentQuat;
+                quats[i].inverse();
+            }
+
+            //This gives a continuous loop at a rate of 30 fps
+            Alpha alpha = new Alpha(-1, (long)(numKeys/.03));
+            alpha.setStartTime(System.currentTimeMillis());
+            alpha.setDuplicateOnCloneTree(true);
+
+            rotator = new RotPosPathInterpolator(alpha, targetGroup, 
+                    axisOfTransform, knots, 
+                    quats, points);
+        }
+        return rotator;
+    }
+
+    public void makeTwoListsTheSameSize(List list1, List list2)
+    {
+        growList(list2.size() - 1, list1);
+        growList(list1.size() - 1, list2);
+    }
+
+    /**
+     * Make sure the list is at least able to
+     * hold a value at index.
+     * @param index an int specifying the initial size
+     * @parame the list that may need to grow
+     */
+    public void growList(int index, List list)
+    {
+        int numNeeded = (index + 1) - list.size();
+        while(numNeeded-- > 0)
+        {
+            list.add(null);
+        }
+    }
+
+    /**
+     * Sets the center of the bounding box that the pivot
+     * should offset.
+     */
+    public void setPivotCenter(Point3f center)
+    {
+        this.pivotCenter = center;
+    }
+
+    /**
+     * Called to set the coordinate system transform for an object named
+     * objectName. 
+     * This is the first t
+     */
+    public void setCoordinateSystem(String objectName, Transform3D coordinateSystem)
+    {
+        namedObjectCoordinateSystems.put(objectName, coordinateSystem);
+    }
+
+    /**
+     * Sets the group that will be used to center rotations.
+     * This is applied to the mesh after all other transforms
+     * have been applied.
+     * @param group the group that will act as the rotation transform.
+     */
+    public void setRotation(Quat4f rotation)
+    {
+        this.rotation = rotation;
+    }
+
+    /**
+     * Sets the pivot that will be used to as a pivot for
+     * these transfomations.
+     * @param group the group that will act as the pivot. 
+     */
+    public void setPivot(Vector3f pivot)
+    {
+        this.pivot = pivot;
+    }
+
+    /**
+     * Sets the scale for x y and z axis for objects. 
+     * This is applied to the mesh before the rotation transform 
+     * has been applied.
+     * @param group the group that will act as the scale 
+     */
+    public void setScale(Vector3f scale)
+    {
+        this.scale = scale;
+    }
+
+    /**
+     * Sets the scale information necessary for animation.s
+     * @param scaleKeys a list of Vector3f, which may contain null elements,
+     * containing a position for some keys.
+     */
+    public void setScaleKeys(List scaleKeys)
+    {
+        this.scaleKeys = scaleKeys;
+    }
+
+    /**
+     * Sets the group that will be used to translate the mesh..
+     * This is applied to the mesh just before the rotation transform 
+     * has been applied.
+     * @param group the group that will act as the position transform.
+     */
+    public void setPosition(Point3f position)
+    {
+        this.position = position;
+    }
+
+    /**
+     * Sets the position information necessary for animation.s
+     * @param positions a list of Point3f, which may contain null elements,
+     * containing a position for some keys.
+     */
+    public void setPositionKeys(List positions)
+    {
+        positionKeys = positions;
+    }
+
+    /**
+     * Sets the orientation information necessary for animation.s
+     * @param positions a list of Quat4f, which may contain null elements,
+     * containing a orientation for some keys.
+     */
+    public void setOrientationKeys(List orientations)
+    {
+        orientationKeys = orientations;
+    }
+
+    /**
+     *
+     */
+    public void setDummyObject(Group object)
+    {
+        dummyObject = object;
+    }
+
+    /**
+     * returns true if position keys and orientation
+     * keys are longer than one element each.
+     */
+    public boolean hasKeys()
+    {
+        return (positionKeys.size() > 1 || orientationKeys.size() > 1);
+    }
+
+    /**
+     */
+    public void addFather(int fatherID, TransformGroup newFather)
+    {
+        if(fatherID < 0)
+        {
+            father = null;
+        }
+        else
+        {
+            father = (TransformGroup)(fatherMap.get(new Integer(fatherID)));
+            //Remove the father's father because the father will
+            //be inserted somewhere later.
+            Group grandFather = (Group)father.getParent();
+            if(grandFather != null)
+            {
+                grandFather.removeChild(father);
+            }
+        }
+        fatherMap.put(id, newFather);
+    }
+
+    /**
+     * Sets the id for these frames.
+     * @param id the id for these frames.
+     */
+    public void setID(int id)
+    {
+        this.id = new Integer(id);
+    }
+}
diff --git a/com/microcrowd/loader/java3d/max3ds/package.html b/com/microcrowd/loader/java3d/max3ds/package.html
new file mode 100644
index 0000000..7cc16ac
--- /dev/null
+++ b/com/microcrowd/loader/java3d/max3ds/package.html
@@ -0,0 +1,3 @@
+<html><body>
+        Contains classes used to load 3d studio max files.
+</body></html>
diff --git a/com/realvue/sim/ui/loader/java3d/max3ds/Loader3DS.java b/com/realvue/sim/ui/loader/java3d/max3ds/Loader3DS.java
new file mode 100644
index 0000000..f166d50
--- /dev/null
+++ b/com/realvue/sim/ui/loader/java3d/max3ds/Loader3DS.java
@@ -0,0 +1,34 @@
+/*
+ * Microcrowd.com
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ * 
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ * 
+ * Contact Josh DeFord jdeford@microcrowd.com
+ */
+package com.realvue.sim.ui.loader.java3d.max3ds;
+
+
+/**
+ * @author jdeford
+ *
+ * Fonzie makes the windmill go round.
+ */
+public class Loader3DS
+{
+    static
+    {
+        System.err.println("com.realvue.sim.ui.loader.java3d.max3ds.Loader has been replaced by com.microcrowd.loader.java3d.max3ds.Loader3DS. Just change the package and it will work.");
+    }
+}

--
Gitblit v1.6.2