summaryrefslogtreecommitdiffstats
path: root/src/classes/share/com/sun/j3d/loaders
diff options
context:
space:
mode:
authorKevin Rushforth <[email protected]>2004-06-09 04:13:21 +0000
committerKevin Rushforth <[email protected]>2004-06-09 04:13:21 +0000
commit7fad83338964ecd32303d6e2737b19963492ecfe (patch)
tree886fd0b010966b11d6f25865715de4d5aa75cbb6 /src/classes/share/com/sun/j3d/loaders
parent4fc115fa7dfc9fe2d614a5e7d3a0db49431930cf (diff)
Initial creation of j3d-core-utils sources in CVS repository
git-svn-id: https://svn.java.net/svn/j3d-core-utils~svn/trunk@5 9497e636-51bd-65ba-982d-a4982e1767a5
Diffstat (limited to 'src/classes/share/com/sun/j3d/loaders')
-rw-r--r--src/classes/share/com/sun/j3d/loaders/IncorrectFormatException.java62
-rw-r--r--src/classes/share/com/sun/j3d/loaders/Loader.java176
-rw-r--r--src/classes/share/com/sun/j3d/loaders/LoaderBase.java147
-rw-r--r--src/classes/share/com/sun/j3d/loaders/ParsingErrorException.java62
-rw-r--r--src/classes/share/com/sun/j3d/loaders/Scene.java148
-rw-r--r--src/classes/share/com/sun/j3d/loaders/SceneBase.java311
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/DebugOutput.java85
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/EnvelopeHandler.java133
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/FloatValueInterpolator.java171
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/ImageScaler.java148
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/J3dLwoParser.java601
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/LWOBFileReader.java266
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/LightIntensityPathInterpolator.java101
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/Lw3dLoader.java706
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/LwLightObject.java100
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/LwoParser.java424
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/LwoSurface.java316
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/LwoTexture.java284
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/LwsBackground.java163
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/LwsCamera.java179
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/LwsEnvelope.java160
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/LwsEnvelopeFrame.java105
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/LwsEnvelopeLightIntensity.java153
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/LwsFog.java143
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/LwsFrame.java341
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/LwsLight.java269
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/LwsMotion.java688
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/LwsObject.java570
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/LwsPrimitive.java68
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/ParserObject.java91
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/SequenceLine.java269
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/SequenceReader.java172
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/ShapeHolder.java267
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/SwitchPathInterpolator.java129
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/TargaReader.java199
-rw-r--r--src/classes/share/com/sun/j3d/loaders/lw3d/TextfileParser.java245
-rw-r--r--src/classes/share/com/sun/j3d/loaders/objectfile/DefaultMaterials.java952
-rw-r--r--src/classes/share/com/sun/j3d/loaders/objectfile/ObjectFile.java1334
-rw-r--r--src/classes/share/com/sun/j3d/loaders/objectfile/ObjectFileMaterials.java425
-rw-r--r--src/classes/share/com/sun/j3d/loaders/objectfile/ObjectFileParser.java179
-rw-r--r--src/classes/share/com/sun/j3d/loaders/objectfile/RgbFile.java202
41 files changed, 11544 insertions, 0 deletions
diff --git a/src/classes/share/com/sun/j3d/loaders/IncorrectFormatException.java b/src/classes/share/com/sun/j3d/loaders/IncorrectFormatException.java
new file mode 100644
index 0000000..3ab53d8
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/IncorrectFormatException.java
@@ -0,0 +1,62 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders;
+
+
+/**
+ * Exception used to indicate that a file of the incorrect
+ * type was passed to a loader.
+ */
+public class IncorrectFormatException extends RuntimeException {
+
+ public IncorrectFormatException() {
+ super();
+ }
+
+ public IncorrectFormatException(String s) {
+ super(s);
+ }
+}
+
diff --git a/src/classes/share/com/sun/j3d/loaders/Loader.java b/src/classes/share/com/sun/j3d/loaders/Loader.java
new file mode 100644
index 0000000..aeab387
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/Loader.java
@@ -0,0 +1,176 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders;
+
+import java.net.URL;
+import java.io.Reader;
+import java.io.FileNotFoundException;
+
+/**
+ * The Loader interface is used to specify the location
+ * and elements of a file format to load.
+ * The interface is used to give loaders of various
+ * file formats a common public interface. Ideally
+ * the Scene interface will be implemented to give
+ * the user a consistent interface to extract the
+ * data.
+ *
+ * @see com.sun.j3d.loaders.Scene
+ */
+public interface Loader {
+
+ // These are the values to be used in constructing the
+ // load flags for the loader. Users should OR the selected
+ // values together to construct an aggregate flag integer
+ // (see the setFlags() method). Users wishing to load all
+ // data in a file should use the LOAD_ALL specifier.
+
+ /** This flag enables the loading of light objects into the scene.*/
+ public static final int LOAD_LIGHT_NODES = 1;
+
+ /** This flag enables the loading of fog objects into the scene.*/
+ public static final int LOAD_FOG_NODES = 2;
+
+ /** This flag enables the loading of background objects into the scene.*/
+ public static final int LOAD_BACKGROUND_NODES = 4;
+
+ /** This flag enables the loading of behaviors into the scene.*/
+ public static final int LOAD_BEHAVIOR_NODES = 8;
+
+ /** This flag enables the loading of view (camera) objects into
+ * the scene.*/
+ public static final int LOAD_VIEW_GROUPS = 16;
+
+ /** This flag enables the loading of sound objects into the scene.*/
+ public static final int LOAD_SOUND_NODES = 32;
+
+ /** This flag enables the loading of all objects into the scene.*/
+ public static final int LOAD_ALL = 0xffffffff;
+
+
+ // Loading methods
+
+ /**
+ * This method loads the named file and returns the Scene
+ * containing the scene. Any data files referenced by this
+ * file should be located in the same place as the named file;
+ * otherwise users should specify an alternate base path with
+ * the setBasePath(String) method.
+ */
+ public Scene load(String fileName) throws FileNotFoundException,
+ IncorrectFormatException, ParsingErrorException;
+
+ /**
+ * This method loads the named file and returns the Scene
+ * containing the scene. Any data files referenced by the Reader
+ * should be located in the same place as the named file; otherwise,
+ * users should specify an alternate base path with the setBaseUrl(URL)
+ * method.
+ */
+ public Scene load(URL url) throws FileNotFoundException,
+ IncorrectFormatException, ParsingErrorException;
+
+ /**
+ * This method loads the Reader and returns the Scene
+ * containing the scene. Any data files referenced by the Reader should
+ * be located in the user's current working directory.
+ */
+ public Scene load(Reader reader)
+ throws FileNotFoundException, IncorrectFormatException,
+ ParsingErrorException;
+
+
+ // Variable get/set methods
+
+ /**
+ * This method sets the base URL name for data files associated with
+ * the file passed into the load(URL) method.
+ * The basePath should be null by default, which is an indicator
+ * to the loader that it should look for any associated files starting
+ * from the same directory as the file passed into the load(URL) method.
+ */
+ public void setBaseUrl(URL url);
+
+ /**
+ * This method sets the base path name for data files associated with
+ * the file passed into the load(String) method.
+ * The basePath should be null by default, which is an indicator
+ * to the loader that it should look for any associated files starting
+ * from the same directory as the file passed into the load(String)
+ * method.
+ */
+ public void setBasePath(String pathName);
+
+ /**
+ * Returns the current base URL setting. By default this is null,
+ * implying the loader should look for associated files starting
+ * from the same directory as the file passed into the load(URL) method.
+ */
+ public URL getBaseUrl();
+
+ /**
+ * Returns the current base path setting. By default this is null,
+ * implying the loader should look for associated files starting
+ * from the same directory as the file passed into the load(String)
+ * method.
+ */
+ public String getBasePath();
+
+ /**
+ * This method sets the load flags for the file. The flags should
+ * equal 0 by default (which tells the loader to only load geometry).
+ * To enable the loading of any particular scene elements, pass
+ * in a logical OR of the LOAD values specified above.
+ */
+ public void setFlags(int flags);
+
+ /**
+ * Returns the current loading flags setting.
+ */
+ public int getFlags();
+}
+
+
+
diff --git a/src/classes/share/com/sun/j3d/loaders/LoaderBase.java b/src/classes/share/com/sun/j3d/loaders/LoaderBase.java
new file mode 100644
index 0000000..ba2d92e
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/LoaderBase.java
@@ -0,0 +1,147 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders;
+
+import java.net.URL;
+import java.io.Reader;
+
+/**
+ * This class implements the Loader interface. To use
+ * a file loader would extend this class.
+ */
+public abstract class LoaderBase implements Loader {
+
+ /** Stores the types of objects that the user wishes to load.*/
+ protected int loadFlags = 0;
+
+ /** Stores the baseUrl for data files associated with the URL
+ * passed into load(URL).*/
+ protected URL baseUrl = null;
+
+ /** Stores the basePath for data files associated with the file
+ * passed into load(String).*/
+ protected String basePath = null;
+
+ // Constructors
+
+ /**
+ * Constructs a Loader with default values for all variables.
+ */
+ public LoaderBase() {
+ }
+
+ /**
+ * Constructs a Loader with the specified flags word.
+ */
+ public LoaderBase(int flags) {
+ loadFlags = flags;
+ }
+
+
+ // Variable get/set methods
+
+ /**
+ * This method sets the base URL name for data files associated with
+ * the file. The baseUrl should be null by default, which is an indicator
+ * to the loader that it should look for any associated files starting
+ * from the same place as the URL passed into the load(URL) method.
+ * Note: Users of setBaseUrl() would then use load(URL)
+ * as opposed to load(String).
+ */
+ public void setBaseUrl(URL url) {
+ baseUrl = url;
+ }
+
+ /**
+ * This method sets the base path name for data files associated with
+ * the file. The basePath should be null by default, which is an indicator
+ * to the loader that it should look for any associated files starting
+ * from the same directory as the file passed into the load(String)
+ * method.
+ * Note: Users of setBasePath() would then use load(String)
+ * as opposed to load(URL).
+ */
+ public void setBasePath(String pathName) {
+ basePath = pathName;
+ }
+
+ /**
+ * Returns the current base URL setting.
+ */
+ public URL getBaseUrl() {
+ return baseUrl;
+ }
+
+ /**
+ * Returns the current base path setting.
+ */
+ public String getBasePath() {
+ return basePath;
+ }
+
+ /**
+ * This method sets the load flags for the file. The flags should
+ * equal 0 by default (which tells the loader to only load geometry).
+ */
+ public void setFlags(int flags) {
+ loadFlags = flags;
+ }
+
+ /**
+ * Returns the current loading flags setting.
+ */
+ public int getFlags() {
+ return loadFlags;
+ }
+
+}
+
+
+
+
+
+
+
+
diff --git a/src/classes/share/com/sun/j3d/loaders/ParsingErrorException.java b/src/classes/share/com/sun/j3d/loaders/ParsingErrorException.java
new file mode 100644
index 0000000..7d94002
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/ParsingErrorException.java
@@ -0,0 +1,62 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders;
+
+
+/**
+ * Exception used to indicate that the loader encountered
+ * a problem parsing the specified file.
+ */
+public class ParsingErrorException extends RuntimeException {
+
+ public ParsingErrorException() {
+ super();
+ }
+
+ public ParsingErrorException(String s) {
+ super(s);
+ }
+}
+
diff --git a/src/classes/share/com/sun/j3d/loaders/Scene.java b/src/classes/share/com/sun/j3d/loaders/Scene.java
new file mode 100644
index 0000000..34f3399
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/Scene.java
@@ -0,0 +1,148 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders;
+
+import java.util.Hashtable;
+
+import javax.media.j3d.Behavior;
+import javax.media.j3d.BranchGroup;
+import javax.media.j3d.TransformGroup;
+import javax.media.j3d.Light;
+import javax.media.j3d.Background;
+import javax.media.j3d.Fog;
+import javax.media.j3d.Sound;
+
+
+/**
+ * The Scene interface is a set of methods used to extract
+ * Java 3D scene graph information from a file loader utility.
+ * The interface is used to give loaders of various
+ * file formats a common public interface.
+ */
+public interface Scene {
+
+
+ /**
+ * This method returns the BranchGroup containing the overall
+ * scene loaded by the loader. All enabled items will be loaded
+ * into this scene except for Behaviors (the Behavior group must be
+ * retrieved separately so that users can choose whether and when
+ * to activate behaviors).
+ */
+ public BranchGroup getSceneGroup();
+
+ /**
+ * This method returns an array of all View Groups defined in the file.
+ * Each View Group is a TransformGroup that is already placed within
+ * the scene that is returned in the getSceneGroup() call. This
+ * TransformGroup holds the position/orientation of the view
+ * as defined by the file. A user might request these references to
+ * the groups in order to look at the data stored there or
+ * to place ViewPlatforms within these groups and allow the
+ * View to activate these ViewPlatforms so that the user would
+ * see the scene from the viewpoints defined in the file.
+ */
+ public TransformGroup[] getViewGroups();
+
+ /**
+ * This method returns an array of floats with the horizontal field
+ * of view. The entries in the array will correspond to those in the
+ * array returned by the method getViewGroups. The entries from these
+ * two arrays together provide all the information needed to recreate
+ * the viewing parameters associated with a scene graph.
+ */
+ public float[] getHorizontalFOVs();
+
+ /**
+ * This method returns an array of all Lights defined in the file.
+ * If no lights are defined, null is returned.
+ */
+ public Light[] getLightNodes();
+
+ /**
+ * This method returns a Hashtable which contains a list of all named
+ * objects in the file and their associated scene graph objects. The
+ * naming scheme for file objects is file-type dependent, but may include
+ * such names as the DEF names of Vrml or filenames of objects (as
+ * in Lightwave 3D). If no named objects are defined, null is returned.
+ */
+ public Hashtable getNamedObjects();
+
+ /**
+ * This method returns an array of all Background nodes defined in the
+ * file. IF no Background nodes are defined, null is returned.
+ */
+ public Background[] getBackgroundNodes();
+
+ /**
+ * This method returns an array of all Fog nodes defined in the
+ * file. If no fog nodes are defined, null is returned.
+ */
+ public Fog[] getFogNodes();
+
+ /**
+ * This method returns an array of all the behavior nodes
+ * in the scene. If no Behavior nodes are defined, null is returned.
+ */
+ public Behavior[] getBehaviorNodes();
+
+ /**
+ * This method returns an array of all of the Sound nodes defined
+ * in the file. If no Sound nodes are defined, null is returned.
+ */
+ public Sound[] getSoundNodes();
+
+ /**
+ * This method returns the text description of the file. If no
+ * such description exists, this method should return null.
+ */
+ public String getDescription();
+
+}
+
+
+
+
+
diff --git a/src/classes/share/com/sun/j3d/loaders/SceneBase.java b/src/classes/share/com/sun/j3d/loaders/SceneBase.java
new file mode 100644
index 0000000..05f6413
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/SceneBase.java
@@ -0,0 +1,311 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders;
+
+import java.lang.Float;
+
+import java.util.Hashtable;
+import java.util.Vector;
+import java.util.Enumeration;
+
+import javax.media.j3d.Behavior;
+import javax.media.j3d.BranchGroup;
+import javax.media.j3d.TransformGroup;
+import javax.media.j3d.Light;
+import javax.media.j3d.Background;
+import javax.media.j3d.Fog;
+import javax.media.j3d.Sound;
+
+
+/**
+ * This class implements the Scene interface and extends it to incorporate
+ * utilities that could be used by loaders. There should be little need
+ * for future loaders to subclass this, or to implement Scene directly,
+ * as the functionality of a SceneBase is fairly straightforward. This
+ * class is responsible for both the storage and retrieval of data from
+ * the Scene. The storage methods (used only by Loader writers) are all
+ * of the add* routines. The retrieval methods (used primarily by Loader
+ * users) are all of the get* routines.
+ */
+public class SceneBase implements Scene {
+
+ BranchGroup sceneGroup = null;
+ BranchGroup behaviorGroup = null;
+ Hashtable namedObjects = new Hashtable();
+ String description = null;
+
+ Vector viewVector = new Vector();
+ Vector hfovVector = new Vector();
+ Vector behaviorVector = new Vector();
+ Vector lightVector = new Vector();
+ Vector fogVector = new Vector();
+ Vector backgroundVector = new Vector();
+ Vector soundVector = new Vector();
+
+ // Add methods
+
+ /**
+ * Sets the sceneGroup to be the group that is passed in.
+ */
+ public void setSceneGroup(BranchGroup scene) {
+ sceneGroup = scene;
+ }
+
+ /**
+ * Adds the given group to the list of view groups.
+ */
+ public void addViewGroup(TransformGroup tg) {
+ viewVector.addElement(tg);
+ }
+
+ /**
+ * Adds the given field of view value to the list of field of view values.
+ */
+ public void addHorizontalFOV(float hfov) {
+ hfovVector.addElement(new Float(hfov));
+ }
+
+ /**
+ * Adds the given behavior to a list of behaviors
+ */
+ public void addBehaviorNode(Behavior b) {
+ behaviorVector.addElement(b);
+ }
+
+ /**
+ * Adds the given Light node to the list of lights.
+ */
+ public void addLightNode(Light light) {
+ lightVector.addElement(light);
+ }
+
+ /**
+ * Adds the given Background node to the list of backgrounds.
+ */
+ public void addBackgroundNode(Background background) {
+ backgroundVector.addElement(background);
+ }
+
+ /**
+ * Adds the given Sound node to the list of sounds.
+ */
+ public void addSoundNode(Sound sound) {
+ soundVector.addElement(sound);
+ }
+
+ /**
+ * Adds the given Fog node to the list of fog nodes.
+ */
+ public void addFogNode(Fog fog) {
+ fogVector.addElement(fog);
+ }
+
+ /**
+ * Sets the text description of the scene to the passed in String.
+ */
+ public void addDescription(String descriptionString) {
+ description = descriptionString;
+ }
+
+ /**
+ * Adds the given String/Object pair to the table of named objects.
+ */
+ public void addNamedObject(String name, Object object) {
+ if (namedObjects.get(name) == null)
+ namedObjects.put(name, object);
+ else {
+ // key already exists - append a unique integer to end of name
+ int nameIndex = 1;
+ boolean done = false;
+ while (!done) {
+ // Iterate starting from "[1]" until we find a unique key
+ String tempName = name + "[" + nameIndex + "]";
+ if (namedObjects.get(tempName) == null) {
+ namedObjects.put(tempName, object);
+ done = true;
+ }
+ nameIndex++;
+ }
+ }
+ }
+
+ /**
+ * This method returns the BranchGroup containing the overall
+ * scene loaded by the loader.
+ */
+ public BranchGroup getSceneGroup() {
+ return sceneGroup;
+ }
+
+
+ /**
+ * This method returns an array of all View Groups defined in the file.
+ * A View Group is defined as a TransformGroup which contains a
+ * ViewPlatform. The TransformGroup holds the position/orientation
+ * information for the given ViewPlatform and the ViewPlatform
+ * holds an view-specific information, such as Field of View.
+ */
+ public TransformGroup[] getViewGroups() {
+ if (viewVector.isEmpty())
+ return null;
+ TransformGroup[] viewGroups = new TransformGroup[viewVector.size()];
+ viewVector.copyInto(viewGroups);
+ return viewGroups;
+ }
+
+ /**
+ * This method returns an array of floats that contains the horizontal
+ * field of view values for each corresponding entry in the array of
+ * view groups returned by the method getViewGroups.
+ */
+ public float[] getHorizontalFOVs() {
+ if (hfovVector.isEmpty())
+ return null;
+
+ int arraySize = hfovVector.size();
+ float[] hfovs = new float[arraySize];
+ Float[] tmpFovs = new Float[hfovVector.size()];
+ hfovVector.copyInto(tmpFovs);
+
+ // copy to array of floats and delete Floats
+ for (int i=0; i<arraySize; i++) {
+ hfovs[i] = tmpFovs[i].floatValue();
+ tmpFovs[i] = null;
+ }
+ return hfovs;
+ }
+
+
+ /**
+ * This method returns an array of all Lights defined in the file.
+ */
+ public Light[] getLightNodes() {
+ if (lightVector.isEmpty())
+ return null;
+ Light[] lightNodes = new Light[lightVector.size()];
+ lightVector.copyInto(lightNodes);
+ return lightNodes;
+ }
+
+
+ /**
+ * This method returns a Hashtable which contains a list of all named
+ * objects in the file and their associated scene graph objects. The
+ * naming scheme for file objects is file-type dependent, but may include
+ * such names as the DEF names of Vrml or filenames of subjects (as
+ * in Lightwave 3D).
+ */
+ public Hashtable getNamedObjects() {
+ return namedObjects;
+ }
+
+
+ /**
+ * This method returns an array of all Background nodes defined in the
+ * file.
+ */
+ public Background[] getBackgroundNodes() {
+ if (backgroundVector.isEmpty())
+ return null;
+ Background[] backgroundNodes = new Background[backgroundVector.size()];
+ backgroundVector.copyInto(backgroundNodes);
+ return backgroundNodes;
+ }
+
+
+ /**
+ * This method returns an array of all Fog nodes defined in the
+ * file.
+ */
+ public Fog[] getFogNodes() {
+ if (fogVector.isEmpty())
+ return null;
+ Fog[] fogNodes = new Fog[fogVector.size()];
+ fogVector.copyInto(fogNodes);
+ return fogNodes;
+ }
+
+
+ /**
+ * This method returns a group containing all of the Behavior nodes
+ * in the scene.
+ */
+ public Behavior[] getBehaviorNodes() {
+ if (behaviorVector.isEmpty())
+ return null;
+ Behavior[] behaviorNodes = new Behavior[behaviorVector.size()];
+ behaviorVector.copyInto(behaviorNodes);
+
+ return behaviorNodes;
+ }
+
+
+ /**
+ * This method returns an array of all of the Sound nodes defined
+ * in the file.
+ */
+ public Sound[] getSoundNodes() {
+ if (soundVector.isEmpty())
+ return null;
+ Sound[] soundNodes = new Sound[soundVector.size()];
+ soundVector.copyInto(soundNodes);
+ return soundNodes;
+ }
+
+
+ /**
+ * This method returns the text description of the file. If no
+ * such description exists, this method should return null.
+ */
+ public String getDescription() {
+ return description;
+ }
+
+}
+
+
+
+
+
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/DebugOutput.java b/src/classes/share/com/sun/j3d/loaders/lw3d/DebugOutput.java
new file mode 100644
index 0000000..544b46b
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/DebugOutput.java
@@ -0,0 +1,85 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+
+/**
+ * This class has utility functions for printing out debugging information.
+ */
+
+class DebugOutput {
+
+ int validOutput = 0;
+ final static int TRACE = 1, VALUES = 2, MISC = 4, LINE_TRACE = 8;
+ final static int NONE = 0, EXCEPTION = 16, TIME = 32, WARNING = 64;
+
+ DebugOutput(int outputTypes) {
+ validOutput = outputTypes;
+ }
+
+ void setValidOutput(int outputTypes) {
+ validOutput = outputTypes;
+ }
+
+ int getValidOutput() {
+ return validOutput;
+ }
+
+ void print(int outputType, String theOutput) {
+ if ((outputType & validOutput) > 0) {
+ System.out.print(theOutput);
+ }
+ }
+
+ void println(int outputType, String theOutput) {
+ if ((outputType & validOutput) > 0)
+ System.out.println(theOutput);
+ }
+
+}
+
+
+
+
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/EnvelopeHandler.java b/src/classes/share/com/sun/j3d/loaders/lw3d/EnvelopeHandler.java
new file mode 100644
index 0000000..faad047
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/EnvelopeHandler.java
@@ -0,0 +1,133 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+import java.io.StreamTokenizer;
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import com.sun.j3d.loaders.ParsingErrorException;
+import java.lang.ClassNotFoundException;
+import java.lang.InstantiationException;
+import java.lang.IllegalAccessException;
+import java.lang.reflect.InvocationTargetException;
+
+
+/**
+ * This class is used in implementing Envelope objects (of which there
+ * is currently only one - LightIntensity).
+ * The class is called whenever the parser has encountered
+ * a token which could have an envelope description. If the
+ * token simply has a numeric value, this value is stored.
+ * If, instead, there is an envelope, then the class creates
+ * the envelope class and parses that information.
+ */
+
+class EnvelopeHandler extends TextfileParser {
+
+ float theValue = 0;
+ boolean hasValue = false;
+ boolean hasEnvelope = true;
+ LwsEnvelope theEnvelope = null;
+
+ /**
+ * Constructor: This constructor is used if there is no existing
+ * implementation for this type of envelope. The real constructor
+ * is called with the generic LwsEnvelope class name, which will
+ * allow s to parse and ignore the envelope data
+ */
+ EnvelopeHandler(StreamTokenizer st,
+ int totalFrames, float totalTime) {
+ this(st, totalFrames, totalTime,
+ "com.sun.j3d.utils.loaders.lw3d.LwsEnvelope");
+ }
+
+ /**
+ * Constructor: This constructor is called with the name of a class
+ * that can handle the encountered envelope. This is done so that this
+ * EnvelopeHandler class can generically call the given class to handle
+ * the envelope, whether it results in parsing/ignoring the data or
+ * in actually using the data
+ */
+ EnvelopeHandler(StreamTokenizer st,
+ int totalFrames,
+ float totalTime,
+ String envClassName) throws ParsingErrorException {
+ try {
+ theValue = (float)getNumber(st);
+ hasValue = true;
+ }
+ catch (NumberFormatException e) {
+ if (st.ttype == '(') {
+ st.pushBack();
+ // This code creates a new instance for the given class name
+ try {
+ Class envClass = Class.forName(envClassName);
+ Constructor constructors[] = envClass.getConstructors();
+ Constructor con = constructors[0];
+ Object args[] = new Object[3];
+ args[0] = (Object)st;
+ args[1] = (Object)(new Integer(totalFrames));
+ args[2] = (Object)(new Float(totalTime));
+ try {
+ theEnvelope = (LwsEnvelope)con.newInstance(args);
+ hasEnvelope = true;
+ }
+ catch (InstantiationException e3) {
+ // Ignore
+ }
+ catch (IllegalAccessException e3) {
+ // Ignore
+ }
+ catch (InvocationTargetException e3) {
+ // Ignore
+ }
+ }
+ catch (ClassNotFoundException e2) {
+ // Ignore
+ }
+ }
+ }
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/FloatValueInterpolator.java b/src/classes/share/com/sun/j3d/loaders/lw3d/FloatValueInterpolator.java
new file mode 100644
index 0000000..c270c0f
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/FloatValueInterpolator.java
@@ -0,0 +1,171 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+import javax.vecmath.*;
+import java.util.BitSet;
+import java.util.Enumeration;
+
+import javax.media.j3d.Alpha;
+import javax.media.j3d.Node;
+import javax.media.j3d.NodeReferenceTable;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.Interpolator;
+import com.sun.j3d.internal.J3dUtilsI18N;
+
+/**
+ * This class acts as an interpolator between values specified in a
+ * floating point array, based on knot values (keyframes) specified in a
+ * knots array.
+ */
+abstract class FloatValueInterpolator extends Interpolator {
+
+ private float knots[];
+ private int knotsLength;
+ protected int currentKnotIndex;
+ protected float currentInterpolationRatio;
+ protected float values[];
+ protected float currentValue;
+
+ /**
+ * Constructs a new FloatValueInterpolator object.
+ * @param alpha the alpha object for this interpolator
+ * @param knots an array of knot values that specify a spline
+ */
+ FloatValueInterpolator(Alpha alpha, float k[], float v[]) {
+
+ super(alpha);
+
+ // Check that first knot = 0.0f
+ knotsLength = k.length;
+ if (k[0] < -0.0001 || k[0] > 0.0001) {
+ throw new IllegalArgumentException(J3dUtilsI18N.getString("FloatValueInterpolator0"));
+ }
+
+ // Check that last knot = 1.0f
+ if ((k[knotsLength-1] - 1.0f) < -0.0001 ||
+ (k[knotsLength-1] - 1.0f) > 0.0001) {
+
+ throw new IllegalArgumentException(J3dUtilsI18N.getString("FloatValueInterpolator1"));
+ }
+
+ // Check to see that knots are in ascending order and copy them
+ this.knots = new float[knotsLength];
+ for (int i = 0; i < knotsLength; i++) {
+ if ((i > 0) && (k[i] < k[i-1])) {
+ throw new IllegalArgumentException(J3dUtilsI18N.getString("FloatValueInterpolator2"));
+ }
+ this.knots[i] = k[i];
+ }
+
+ // check to see that we have the same number of values as knots
+ if (knotsLength != v.length) {
+ throw new IllegalArgumentException(J3dUtilsI18N.getString("FloatValueInterpolator3"));
+ }
+
+ // copy the values
+ this.values = new float[knotsLength];
+ for(int i = 0; i < knotsLength; i++) {
+ this.values[i] = v[i];
+ }
+
+ }
+
+ /**
+ * This method sets the value at the specified index for
+ * this interpolator.
+ * @param index the index to be changed
+ * @param position the new value at index
+ */
+ void setValue(int index, float value) {
+ this.values[index] = value;
+ }
+
+ /**
+ * This method retrieves the value at the specified index.
+ * @param index the index of the value requested
+ * @return the interpolator's value at the index
+ */
+ float getValue(int index) {
+ return this.values[index];
+ }
+
+ /**
+ * This method computes the bounding knot indices and interpolation value
+ * "currentValue" given the current value of alpha, the knots[] array and
+ * the array of values.
+ * If the index is 0 and there will be no interpolation, both the
+ * index variable and the interpolation variable are set to 0.
+ * Otherwise, currentKnotIndex is set to the lower index of the
+ * two bounding knot points and the currentInterpolationRatio
+ * variable is set to the ratio of the alpha value between these
+ * two bounding knot points.
+ */
+ protected void computePathInterpolation() {
+ float alphaValue = (this.getAlpha()).value();
+
+ for (int i = 0; i < knotsLength; i++) {
+ if ((i == 0 && alphaValue <= knots[i]) ||
+ (i > 0 && alphaValue >= knots[i-1] && alphaValue <= knots[i])) {
+
+ if (i==0) {
+ currentInterpolationRatio = 0f;
+ currentKnotIndex = 0;
+ currentValue = values[0];
+ }
+ else {
+ currentInterpolationRatio =
+ (alphaValue - knots[i-1])/(knots[i] - knots[i-1]);
+ currentKnotIndex = i - 1;
+ currentValue = values[i-1] +
+ currentInterpolationRatio * (values[i] - values[i-1]);
+ }
+ break;
+ }
+ }
+ }
+
+}
+
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/ImageScaler.java b/src/classes/share/com/sun/j3d/loaders/lw3d/ImageScaler.java
new file mode 100644
index 0000000..d457f9c
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/ImageScaler.java
@@ -0,0 +1,148 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+
+import java.awt.Image;
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferInt;
+
+/**
+ * This class resizes an image to be the nearest power of 2 wide and high.
+ * This facility now exists inside of the TextureLoader class, so
+ * ImageScaler should be eliminated at some point.
+ */
+
+class ImageScaler {
+
+ int origW, origH;
+ Image origImage;
+
+ ImageScaler(Image image, int w, int h) {
+ origImage = image;
+ origW = w;
+ origH = h;
+ }
+
+ ImageScaler(BufferedImage image) {
+ origImage = image;
+ origW = image.getWidth();
+ origH = image.getHeight();
+ }
+
+ /**
+ * Utility method to return closes poser of 2 to the given integer
+ */
+ int getClosestPowerOf2(int value) {
+
+ if (value < 1)
+ return value;
+
+ int powerValue = 1;
+ for (int i = 1; i < 20; ++i) {
+ powerValue *= 2;
+ if (value < powerValue) {
+ // Found max bound of power, determine which is closest
+ int minBound = powerValue/2;
+ if ((powerValue - value) >
+ (value - minBound))
+ return minBound;
+ else
+ return powerValue;
+ }
+ }
+ // shouldn't reach here...
+ return 1;
+ }
+
+ /**
+ * Returns an Image that has been scaled from the original image to
+ * the closest power of 2
+ */
+ Image getScaledImage() {
+ int newWidth = getClosestPowerOf2(origW);
+ int newHeight = getClosestPowerOf2(origH);
+
+ // If the image is already a power of 2 wide/tall, no need to scale
+ if (newWidth == origW &&
+ newHeight == origH)
+ return origImage;
+
+ Image scaledImage = null;
+
+ if (origImage instanceof BufferedImage) {
+ // If BufferedImage, then we have some work to do
+ BufferedImage origImageB = (BufferedImage)origImage;
+ scaledImage =
+ new BufferedImage(newWidth,
+ newHeight,
+ origImageB.getType());
+ BufferedImage scaledImageB = (BufferedImage)scaledImage;
+ float widthScale = (float)origW/(float)newWidth;
+ float heightScale = (float)origH/(float)newHeight;
+ int origPixels[] = ((DataBufferInt)origImageB.getRaster().getDataBuffer()).getData();
+ int newPixels[] = ((DataBufferInt)scaledImageB.getRaster().getDataBuffer()).getData();
+ for (int row = 0; row < newHeight; ++row) {
+ for (int column = 0; column < newWidth; ++column) {
+ int oldRow = Math.min(origH-1,
+ (int)((float)(row)*
+ heightScale + .5f));
+ int oldColumn =
+ Math.min(origW-1,
+ (int)((float)column*widthScale + .5f));
+ newPixels[row*newWidth + column] =
+ origPixels[oldRow*origW + oldColumn];
+ }
+ }
+ }
+ else {
+ // If regular Image, then the work is done for us
+ scaledImage = origImage.getScaledInstance(newWidth,
+ newHeight,
+ Image.SCALE_DEFAULT);
+ }
+ return scaledImage;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/J3dLwoParser.java b/src/classes/share/com/sun/j3d/loaders/lw3d/J3dLwoParser.java
new file mode 100644
index 0000000..1db2dbb
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/J3dLwoParser.java
@@ -0,0 +1,601 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+import java.awt.Component;
+import java.awt.Image;
+import java.util.Enumeration;
+import java.util.Vector;
+import com.sun.j3d.utils.geometry.GeometryInfo;
+import com.sun.j3d.utils.geometry.NormalGenerator;
+import com.sun.j3d.utils.geometry.Stripifier;
+import com.sun.j3d.utils.image.TextureLoader;
+import com.sun.j3d.loaders.IncorrectFormatException;
+import java.io.FileNotFoundException;
+
+import javax.media.j3d.*;
+import javax.vecmath.*;
+
+import java.net.*;
+
+
+/**
+ * This class is responsible for turning Lightwave geometry data into
+ * Java3D geometry. It is a subclass of LwoObject and calls that
+ * superclass when first instantiated to parse the binary file and turn it
+ * into intermediate data structures. J3dLwoParser then goes through
+ * those data structures and turns them into Java3D objects. For each
+ * ShapeHolder object created by the parent class, this class retrieves
+ * the geometry data and associated surface data, creates the
+ * appropriate Geometry object (usually IndexedTriangleFanArray,
+ * unless the object is points or lines), including calculating normals for
+ * the polygons and sets up the Appearance according to the surface
+ * parameters (including calculating texture coordinates if necessary).
+ */
+
+class J3dLwoParser extends LwoParser {
+
+ float normalCoordsArray[];
+ int normalIndicesArray[];
+ Shape3D objectShape;
+ Color3f color, diffuseColor, specularColor, emissiveColor;
+ float shininess;
+ Vector objectShapeList = new Vector();
+
+ /**
+ * Constructor: Calls LwoObject to parse file and create data structures
+ */
+ J3dLwoParser(String fileName,
+ int debugVals) throws FileNotFoundException {
+ super(fileName, debugVals);
+ }
+
+ J3dLwoParser(URL url, int debugVals)
+ throws FileNotFoundException {
+ super(url, debugVals);
+ }
+
+ void getSurf(int length) throws FileNotFoundException {
+ super.getSurf(length);
+ }
+
+
+ /**
+ * Turns LwoObject's data structures (created from the binary geometry
+ * file) into Java3d objects
+ */
+ void createJava3dGeometry() throws IncorrectFormatException {
+
+ GeometryArray object;
+ LwoTexture texture;
+
+ for (Enumeration e = shapeList.elements();
+ e.hasMoreElements() ;) {
+ int vertexFormat = javax.media.j3d.GeometryArray.COORDINATES;
+ ShapeHolder shape = (ShapeHolder)e.nextElement();
+ debugOutputLn(LINE_TRACE, "about to create Arrays for Shape");
+ debugOutputLn(VALUES, "shape = " + shape);
+ shape.createArrays(true);
+ int vertexCount = shape.coordsArray.length/3;
+ int indexCount = 0;
+ if (shape.facetIndices != null)
+ indexCount = shape.facetIndices.length;
+ debugOutputLn(VALUES, "numSurf = " + shape.numSurf);
+ // Find the right surface. Note: surfaces are indexed by
+ // name. So take this surf number, look up the name of that
+ // surface in surfaceNameList, then take that name and
+ // find the matching LwoSurface
+ String surfName =
+ (String)surfNameList.elementAt(shape.numSurf - 1);
+ LwoSurface surf = null;
+ for (int surfNum = 0;
+ surfNum < surfaceList.size();
+ ++surfNum) {
+ LwoSurface tempSurf =
+ (LwoSurface)surfaceList.elementAt(surfNum);
+ String tempSurfName = tempSurf.surfName;
+ if (surfName.equals(tempSurfName)) {
+ surf = tempSurf;
+ break;
+ }
+ }
+ if (surf == null) {
+ throw new IncorrectFormatException(
+ "bad surf for surfnum/name = " + shape.numSurf + ", " +
+ surfName);
+ }
+ debugOutputLn(VALUES, "surf = " + surf);
+
+ // Get the LwoTexture object (if any) for the surface
+ texture = surf.getTexture();
+
+ Appearance appearance = new Appearance();
+ if (shape.facetSizes[0] == 1) {
+ // This case happens if the objects are points
+ // Note that points are colored, not lit
+ object = new
+ javax.media.j3d.PointArray(vertexCount, vertexFormat);
+ object.setCoordinates(0, shape.coordsArray);
+ ColoringAttributes colorAtt =
+ new ColoringAttributes(surf.getColor(),
+ ColoringAttributes.FASTEST);
+ PointAttributes pointStyle = new PointAttributes();
+ pointStyle.setPointSize(1);
+
+ appearance.setColoringAttributes(colorAtt);
+ appearance.setPointAttributes(pointStyle);
+ }
+ else if (shape.facetSizes[0] == 2) {
+ // This case happens if the objects are lines
+ // Note that lines are colored, not lit
+ debugOutputLn(LINE_TRACE, "Creating IndexedLineArray");
+ object = new javax.media.j3d.LineArray(vertexCount,
+ vertexFormat);
+ object.setCoordinates(0, shape.coordsArray);
+ ColoringAttributes colorAtt =
+ new ColoringAttributes(surf.getColor(),
+ ColoringAttributes.FASTEST);
+ appearance.setColoringAttributes(colorAtt);
+ }
+ else {
+ // This is the case for any polygonal objects
+ debugOutputLn(LINE_TRACE, "Creating IndexedTriFanArray");
+ // create triFanArray
+ vertexFormat |= javax.media.j3d.GeometryArray.NORMALS;
+
+ debugOutputLn(LINE_TRACE, "about to process vertices/indices, facetIndices = " +
+ shape.facetIndices);
+ if (shape.facetIndices != null) {
+ float[] textureCoords = null;
+ int[] textureIndices = null;
+
+ debugOutputLn(LINE_TRACE, "setting vertexCount, normind = " + shape.normalIndices);
+ // If these are null we're going direct (non-indexed)
+ debugOutputLn(LINE_TRACE, "vtxcount, format, indcount = " +
+ vertexCount + ", " + vertexFormat +
+ ", " + indexCount);
+ if (texture != null) {
+ // There's a texture here - need to create the appropriate arrays
+ // and calculate texture coordinates for the object
+ vertexFormat |= GeometryArray.TEXTURE_COORDINATE_2;
+ textureCoords = new float[vertexCount * 2];
+ textureIndices = new int[shape.facetIndices.length];
+ calculateTextureCoords(texture, shape.coordsArray,
+ shape.facetIndices,
+ textureCoords, textureIndices);
+ debugOutputLn(LINE_TRACE, "textureCoords:");
+ debugOutputLn(LINE_TRACE, "texture Coords, Indices.length = " + textureCoords.length + ", " + textureIndices.length);
+ }
+ debugOutputLn(LINE_TRACE, "about to create GeometryInfo");
+
+ // Use the GeometryInfo utility to calculate smooth normals
+ GeometryInfo gi =
+ new GeometryInfo(GeometryInfo.TRIANGLE_FAN_ARRAY);
+ gi.setCoordinates(shape.coordsArray);
+ gi.setCoordinateIndices(shape.facetIndices);
+ gi.setStripCounts(shape.facetSizes);
+ if (texture != null) {
+ gi.setTextureCoordinateParams(1, 2);
+ gi.setTextureCoordinates(0, textureCoords);
+ gi.setTextureCoordinateIndices(0, textureIndices);
+ }
+ gi.recomputeIndices();
+ NormalGenerator ng =
+ new NormalGenerator(surf.getCreaseAngle());
+ ng.generateNormals(gi);
+ Stripifier st = new Stripifier();
+ st.stripify(gi);
+ object = gi.getGeometryArray(true, true, false);
+ debugOutputLn(LINE_TRACE, "done.");
+ }
+ else {
+ // This case is called if LwoObject did not create facet
+ // indices. This code is not currently used because facet
+ // indices are always created for polygonal objects
+ debugOutputLn(LINE_TRACE,
+ "about to create trifanarray with " +
+ "vertexCount, facetSizes.len = " +
+ vertexCount + ", " +
+ shape.facetSizes.length);
+ object = new
+ javax.media.j3d.TriangleFanArray(vertexCount,
+ vertexFormat,
+ shape.facetSizes);
+ object.setCoordinates(0, shape.coordsArray);
+ object.setNormals(0, shape.normalCoords);
+ debugOutputLn(VALUES, "passed in normalCoords, length = " +
+ shape.normalCoords.length);
+ }
+ debugOutputLn(LINE_TRACE, "created fan array");
+
+ // Setup Appearance given the surface parameters
+ Material material = new Material(surf.getColor(),
+ surf.getEmissiveColor(),
+ surf.getDiffuseColor(),
+ surf.getSpecularColor(),
+ surf.getShininess());
+ material.setLightingEnable(true);
+ appearance.setMaterial(material);
+ if (surf.getTransparency() != 0f) {
+ TransparencyAttributes ta = new TransparencyAttributes();
+ ta.setTransparency(surf.getTransparency());
+ ta.setTransparencyMode(ta.BLENDED);
+ appearance.setTransparencyAttributes(ta);
+ }
+ if (texture != null) {
+ debugOutputLn(LINE_TRACE, "texture != null, enable texturing");
+ Texture tex = texture.getTexture();
+ tex.setEnable(true);
+ appearance.setTexture(tex);
+ TextureAttributes ta = new TextureAttributes();
+ if (texture.getType().equals("DTEX"))
+ ta.setTextureMode(TextureAttributes.MODULATE);
+ else if (texture.getType().equals("CTEX"))
+ ta.setTextureMode(TextureAttributes.DECAL);
+ appearance.setTextureAttributes(ta);
+ }
+ else {
+ debugOutputLn(LINE_TRACE, "texture == null, no texture to use");
+ }
+ }
+ debugOutputLn(LINE_TRACE, "done creating object");
+
+ // This does gc
+ shape.nullify();
+
+ objectShape = new Shape3D(object);
+
+ // Combine the appearance and geometry
+ objectShape.setAppearance(appearance);
+ objectShapeList.addElement(objectShape);
+ }
+ }
+
+ /**
+ * Calculate texture coordinates for the geometry given the texture
+ * map properties specified in the LwoTexture object
+ */
+ void calculateTextureCoords(LwoTexture texture,
+ float verts[], int indices[],
+ float[] textureCoords, int[] textureIndices) {
+
+ /*
+ the actual math in these coord calculations comes directly from
+ Newtek - they posted sample code to help compute tex coords based
+ on the type of mapping:
+
+ Here are some simplified code fragments showing how LightWave
+ computes UV coordinates from X, Y, and Z. If the resulting
+ UV coordinates are not in the range from 0 to 1, the
+ appropriate integer should be added to them to bring them into
+ that range (the fract function should have accomplished
+ this by subtracting the floor of each number from itself).
+ Then they can be multiplied by the width and height (in pixels)
+ of an image map to determine which pixel to look up. The
+ texture size, center, and tiling parameters are taken right
+ off the texture control panel.
+
+
+ x -= xTextureCenter;
+ y -= yTextureCenter;
+ z -= zTextureCenter;
+ if (textureType == TT_PLANAR) {
+ s = (textureAxis == TA_X) ? z / zTextureSize + .5 :
+ x / xTextureSize + .5;
+ t = (textureAxis == TA_Y) ? -z / zTextureSize + .5 :
+ -y / yTextureSize + .5;
+ u = fract(s);
+ v = fract(t);
+ }
+ else if (type == TT_CYLINDRICAL) {
+ if (textureAxis == TA_X) {
+ xyztoh(z,x,-y,&lon);
+ t = -x / xTextureSize + .5;
+ }
+ else if (textureAxis == TA_Y) {
+ xyztoh(-x,y,z,&lon);
+ t = -y / yTextureSize + .5;
+ }
+ else {
+ xyztoh(-x,z,-y,&lon);
+ t = -z / zTextureSize + .5;
+ }
+ lon = 1.0 - lon / TWOPI;
+ if (widthTiling != 1.0)
+ lon = fract(lon) * widthTiling;
+ u = fract(lon);
+ v = fract(t);
+ }
+ else if (type == TT_SPHERICAL) {
+ if (textureAxis == TA_X)
+ xyztohp(z,x,-y,&lon,&lat);
+ else if (textureAxis == TA_Y)
+ xyztohp(-x,y,z,&lon,&lat);
+ else
+ xyztohp(-x,z,-y,&lon,&lat);
+ lon = 1.0 - lon / TWOPI;
+ lat = .5 - lat / PI;
+ if (widthTiling != 1.0)
+ lon = fract(lon) * widthTiling;
+ if (heightTiling != 1.0)
+ lat = fract(lat) * heightTiling;
+ u = fract(lon);
+ v = fract(lat);
+ }
+
+ support functions:
+
+ void xyztoh(float x,float y,float z,float *h)
+ {
+ if (x == 0.0 && z == 0.0)
+ *h = 0.0;
+ else {
+ if (z == 0.0)
+ *h = (x < 0.0) ? HALFPI : -HALFPI;
+ else if (z < 0.0)
+ *h = -atan(x / z) + PI;
+ else
+ *h = -atan(x / z);
+ }
+ }
+
+ void xyztohp(float x,float y,float z,float *h,float *p)
+ {
+ if (x == 0.0 && z == 0.0) {
+ *h = 0.0;
+ if (y != 0.0)
+ *p = (y < 0.0) ? -HALFPI : HALFPI;
+ else
+ *p = 0.0;
+ }
+ else {
+ if (z == 0.0)
+ *h = (x < 0.0) ? HALFPI : -HALFPI;
+ else if (z < 0.0)
+ *h = -atan(x / z) + PI;
+ else
+ *h = -atan(x / z);
+ x = sqrt(x * x + z * z);
+ if (x == 0.0)
+ *p = (y < 0.0) ? -HALFPI : HALFPI;
+ else
+ *p = atan(y / x);
+ }
+ }
+ */
+
+ debugOutputLn(TRACE, "calculateTextureCoords()");
+ // Compute texture coord stuff
+ float sx = 0, sz = 0, ty = 0, tz = 0;
+ double s, t;
+ int textureAxis = texture.getTextureAxis();
+ Vector3f textureSize = texture.getTextureSize();
+ Vector3f textureCenter = texture.getTextureCenter();
+
+ String mappingType = texture.getMappingType();
+ if (mappingType.startsWith("Cylindrical"))
+ calculateCylindricalTextureCoords(textureAxis, textureSize,
+ textureCenter, textureCoords,
+ textureIndices,
+ verts, indices);
+ else if (mappingType.startsWith("Spherical"))
+ calculateSphericalTextureCoords(textureAxis,
+ textureCenter, textureCoords,
+ textureIndices,
+ verts, indices);
+ else if (mappingType.startsWith("Planar"))
+ calculatePlanarTextureCoords(textureAxis, textureSize,
+ textureCenter, textureCoords,
+ textureIndices,
+ verts, indices);
+
+ }
+
+ /** See the comments in calculateTextureCoordinates*/
+ double xyztoh(float x,float y,float z) {
+ if (x == 0.0 && z == 0.0)
+ return 0.0;
+ else {
+ if (z == 0.0)
+ return (x < 0.0) ? Math.PI/2.0 : -Math.PI/2.0;
+ else if (z < 0.0)
+ return -Math.atan(x / z) + Math.PI;
+ else
+ return -Math.atan(x / z);
+ }
+ }
+
+ /** See the comments in calculateTextureCoordinates*/
+ double xyztop(float x,float y,float z) {
+ double p;
+
+ if (x == 0.0 && z == 0.0) {
+ if (y != 0.0)
+ p = (y < 0.0) ? -Math.PI/2 : Math.PI/2;
+ else
+ p = 0.0;
+ }
+ else {
+ x = (float)Math.sqrt(x * x + z * z);
+ if (x == 0.0)
+ p = (y < 0.0) ? -Math.PI/2 : Math.PI/2;
+ else
+ p = Math.atan(y / x);
+ }
+ return p;
+ }
+
+
+ /** See the comments in calculateTextureCoordinates*/
+ void calculateSphericalTextureCoords(int textureAxis,
+ Vector3f textureCenter,
+ float textureCoords[],
+ int textureIndices[],
+ float verts[], int indices[]) {
+ debugOutputLn(TRACE, "calculateSphericalTextureCoords");
+ double s, t;
+
+
+ for (int i = 0; i < indices.length; ++i) {
+ float x = verts[3*indices[i]] - textureCenter.x;
+ float y = verts[3*indices[i]+1] - textureCenter.y;
+ float z = -(verts[3*indices[i]+2] + textureCenter.z);
+ if (textureAxis == 1){ // X Axis
+ s = xyztoh(z, x, -y);
+ t = xyztop(z,x,-y);
+ }
+ else if (textureAxis == 2) { // Y Axis
+ s = xyztoh(-x,y,z);
+ t = xyztop(-x,y,z);
+ }
+ else { // Z Axis
+ s = xyztoh(-x,z,-y);
+ t = xyztop(-x,z,-y);
+ }
+ s = 1.0 - s / (2*Math.PI);
+ t = -(.5 - t / Math.PI);
+ textureCoords[indices[i]*2] = (float)s;
+ textureCoords[indices[i]*2 + 1] = (float)t;
+ textureIndices[i] = indices[i];
+ }
+ }
+
+ /** See the comments in calculateTextureCoordinates*/
+ void calculateCylindricalTextureCoords(int textureAxis,
+ Vector3f textureSize,
+ Vector3f textureCenter,
+ float textureCoords[],
+ int textureIndices[],
+ float verts[], int indices[]) {
+ debugOutputLn(TRACE, "calculateCylindricalTextureCoords");
+ debugOutputLn(VALUES, "axis, size, center, tc, ti, v, i = " +
+ textureAxis + ", " +
+ textureSize + ", " +
+ textureCenter + ", " +
+ textureCoords + ", " +
+ textureIndices + ", " +
+ verts + ", " +
+ indices);
+ double s, t;
+
+ debugOutputLn(VALUES, "Cyl Texture Coords:");
+ for (int i = 0; i < indices.length; ++i) {
+ float x = verts[3*indices[i]] - textureCenter.x;
+ float y = verts[3*indices[i]+1] - textureCenter.y;
+ float z = -(verts[3*indices[i]+2] + textureCenter.z);
+ // Negate z value because we invert geom z's to swap handedness
+ if (textureAxis == 1) { // X axis
+ s = xyztoh(z,x,-y);
+ t = x / textureSize.x + .5;
+ }
+ else if (textureAxis == 2) { // Y Axis
+ s = xyztoh(-x,y,z);
+ t = y / textureSize.y + .5;
+ }
+ else {
+ s = xyztoh(-x,z,-y);
+ t = z / textureSize.z + .5;
+ }
+ s = 1.0 - s / (2*Math.PI);
+ textureCoords[indices[i]*2] = (float)s;
+ textureCoords[indices[i]*2 + 1] = (float)t;
+ textureIndices[i] = indices[i];
+ debugOutputLn(VALUES, "x, y, z = " +
+ x + ", " + y + ", " + z + " " +
+ "s, t = " + s + ", " + t);
+ }
+ }
+
+ /** See the comments in calculateTextureCoordinates*/
+ void calculatePlanarTextureCoords(int textureAxis, Vector3f textureSize,
+ Vector3f textureCenter,
+ float textureCoords[],
+ int textureIndices[],
+ float verts[], int indices[]) {
+ debugOutputLn(TRACE, "calculatePlanarTextureCoords");
+ debugOutputLn(VALUES, "size, center, axis = " +
+ textureSize + textureCenter + ", " + textureAxis);
+ float sx = 0, sz = 0, ty = 0, tz = 0;
+ double s, t;
+
+ if (textureAxis == 1) { // X Axis
+ sz = -1.0f / textureSize.z; // Negate because we negate z in geom
+ ty = 1.0f / textureSize.y;
+ }
+ else if (textureAxis == 2) { // Y Axis
+ sx = 1.0f / textureSize.x;
+ tz = -1.0f / textureSize.z; // Negate because we negate z in geom
+ }
+ else { // Z Axis
+ sx = 1.0f / textureSize.x;
+ ty = 1.0f / textureSize.y;
+ }
+
+ debugOutputLn(VALUES, "Planar Texture Coords:");
+ for (int i = 0; i < indices.length; ++i) {
+ float x = verts[3*indices[i]] - textureCenter.x;
+ float y = verts[3*indices[i]+1] - textureCenter.y;
+ float z = verts[3*indices[i]+2] + textureCenter.z;
+ s = x*sx + z*sz + .5;
+ t = y*ty + z*tz + .5;
+ textureCoords[indices[i]*2] = (float)s;
+ textureCoords[indices[i]*2 + 1] = (float)t;
+ textureIndices[i] = indices[i];
+ debugOutputLn(VALUES, "x, y, z = " +
+ x + ", " + y + ", " + z + " " +
+ "s, t = " + s + ", " + t);
+ }
+ }
+
+
+ Shape3D getJava3dShape() {
+ return objectShape;
+ }
+
+ Vector getJava3dShapeList() {
+ return objectShapeList;
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/LWOBFileReader.java b/src/classes/share/com/sun/j3d/loaders/lw3d/LWOBFileReader.java
new file mode 100644
index 0000000..d83bfe4
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/LWOBFileReader.java
@@ -0,0 +1,266 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileInputStream;
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import com.sun.j3d.loaders.ParsingErrorException;
+
+
+class LWOBFileReader extends BufferedInputStream {
+
+
+
+ // Debugging constants
+ final static int TRACE = DebugOutput.TRACE;
+ final static int VALUES = DebugOutput.VALUES;
+ final static int MISC = DebugOutput.MISC;
+ final static int LINE_TRACE = DebugOutput.LINE_TRACE;
+ final static int NONE = DebugOutput.NONE;
+ final static int EXCEPTION = DebugOutput.EXCEPTION;
+
+ protected DebugOutput debugPrinter;
+
+ protected String theFilename;
+
+ protected int marker;
+
+
+
+ protected void debugOutputLn(int outputType, String theOutput) {
+ if (theOutput.equals(""))
+ debugPrinter.println(outputType, theOutput);
+ else
+ debugPrinter.println(outputType,
+ getClass().getName() + "::" + theOutput);
+ } // End of debugOutputLn
+
+
+
+ // Return a string consisting of the next 4 bytes in the file
+ public String getToken() throws ParsingErrorException {
+ byte tokenBuffer[] = new byte[4];
+ try {
+ int readResult = read(tokenBuffer, 0, 4);
+ if (readResult == -1) {
+ debugOutputLn(LINE_TRACE, "no token - returning null");
+ return null;
+ }
+ return new String(tokenBuffer);
+ }
+ catch (IOException e) {
+ debugOutputLn(EXCEPTION, "getToken: " + e);
+ throw new ParsingErrorException(e.getMessage());
+ }
+ }
+
+
+
+ /**
+ * Skip ahead amount bytes in the file
+ */
+ public void skipLength(int amount) throws ParsingErrorException {
+ try {
+ skip((long)amount);
+ marker += amount;
+ }
+ catch (IOException e) {
+ debugOutputLn(EXCEPTION, "skipLength: " + e);
+ throw new ParsingErrorException(e.getMessage());
+ }
+ }
+
+
+
+ /**
+ * Read four bytes from the file and return their integer value
+ */
+ public int getInt() throws ParsingErrorException {
+ try {
+ int x = 0;
+ for (int i = 0 ; i < 4 ; i++) {
+ int readResult = read();
+ if (readResult == -1)
+ throw new ParsingErrorException("Unexpected EOF");
+ x = (x << 8) | readResult;
+ }
+ return x;
+ }
+ catch (IOException e) {
+ debugOutputLn(EXCEPTION, "getInt: " + e);
+ throw new ParsingErrorException(e.getMessage());
+ }
+ }
+
+
+
+ /**
+ * Read four bytes from the file and return their float value
+ */
+ public float getFloat() throws ParsingErrorException {
+ return Float.intBitsToFloat(getInt());
+ } // End of getFloat
+
+
+
+ /**
+ * Returns the name of the file associated with this stream
+ */
+ public String getFilename() {
+ return theFilename;
+ } // End of getFilename
+
+
+
+ /**
+ * Returns a string read from the file. The string is assumed to
+ * end with '0'.
+ */
+ public String getString() throws ParsingErrorException {
+ byte buf[] = new byte[512];
+ try {
+ byte b;
+ int len = 0;
+ do {
+ b = (byte)read();
+ buf[len++] = b;
+ } while (b != 0);
+ // Have to read an even number of bytes
+ if (len % 2 != 0) read();
+ }
+ catch (IOException e) {
+ debugOutputLn(EXCEPTION, "getString: " + e);
+ throw new ParsingErrorException(e.getMessage());
+ }
+ return new String(buf);
+ } // End of getString
+
+
+
+ /**
+ * Reads an array of xyz values.
+ */
+ public void getVerts(float ar[], int num) throws ParsingErrorException {
+ for (int i = 0 ; i < num ; i++) {
+ ar[i * 3 + 0] = getFloat();
+ ar[i * 3 + 1] = getFloat();
+ ar[i * 3 + 2] = -getFloat();
+ }
+ } // End of getVerts
+
+
+
+ /**
+ * Reads two bytes from the file and returns their integer value.
+ */
+ public int getShortInt() throws ParsingErrorException {
+ int i = 0;
+ try {
+ i = read();
+ i = (i << 8) | read();
+ // Sign extension
+ if ((i & 0x8000) != 0) i |= 0xffff0000;
+ }
+ catch (IOException e) {
+ debugOutputLn(EXCEPTION, "getShortInt: " + e);
+ throw new ParsingErrorException(e.getMessage());
+ }
+ return i;
+ } // End of getShortInt
+
+
+
+ /**
+ * Returns the current position in the file
+ */
+ public int getMarker() {
+ // protected field inherited from BufferedInputStream
+ return marker;
+ } // End of getMarker
+
+
+
+ public int read() throws IOException {
+ marker++;
+ return super.read();
+ } // End of read()
+
+
+
+ public int read(byte[] buffer, int offset, int count) throws IOException {
+ int ret = super.read(buffer, offset, count);
+ if (ret != -1) marker += ret;
+ return ret;
+ } // End of read(byte[], int, int)
+
+
+
+ /**
+ * Constructor.
+ */
+ public LWOBFileReader(String filename) throws FileNotFoundException {
+ super(new FileInputStream(filename));
+
+ // Add constants on this line to get more debug output
+ debugPrinter = new DebugOutput(127);
+
+ marker = 0;
+ } // End of constructor
+
+ public LWOBFileReader(java.net.URL url) throws java.io.IOException {
+ super(url.openStream());
+
+ // add constants on this line to get more debug output
+ debugPrinter = new DebugOutput(127);
+
+ marker = 0;
+ }
+
+} // End of file LWOBFileReader
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/LightIntensityPathInterpolator.java b/src/classes/share/com/sun/j3d/loaders/lw3d/LightIntensityPathInterpolator.java
new file mode 100644
index 0000000..61ba2fb
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/LightIntensityPathInterpolator.java
@@ -0,0 +1,101 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+import java.util.*;
+
+import javax.media.j3d.Alpha;
+import javax.media.j3d.Light;
+
+/**
+ * This Interpolator object modifies the intensity of a Light object
+ * according to the keyframes in a light intensity envelope
+ */
+class LightIntensityPathInterpolator extends FloatValueInterpolator {
+
+ LwLightObject theLight;
+
+ LightIntensityPathInterpolator(Alpha alpha,
+ float knots[],
+ float values[],
+ Object target) {
+
+ super(alpha, knots, values);
+ theLight = (LwLightObject)target;
+ }
+
+ /**
+ * This method is invoked by the behavior scheduler every frame. It maps
+ * the alpha value that corresponds to the current time into the
+ * appropriate light intensity for that time as obtained by interpolating
+ * between the light intensity values for each knot point that were passed
+ * to this class.
+ * @param criteria enumeration of criteria that have triggered this wakeup
+ */
+
+ public void processStimulus(Enumeration criteria) {
+ // Handle stimulus
+
+ if (this.getAlpha() != null) {
+
+ // Let FloatValueInterpolator calculate the correct
+ // interpolated value
+ computePathInterpolation();
+
+ // Set light intensity to the value calculated by
+ // FloatValueInterpolator
+ if (theLight != null)
+ theLight.setIntensity(currentValue);
+
+ if ((this.getAlpha()).finished())
+ return;
+ }
+
+ wakeupOn(defaultWakeupCriterion);
+
+ }
+
+}
+
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/Lw3dLoader.java b/src/classes/share/com/sun/j3d/loaders/lw3d/Lw3dLoader.java
new file mode 100644
index 0000000..c783518
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/Lw3dLoader.java
@@ -0,0 +1,706 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+
+import com.sun.j3d.loaders.*;
+import java.awt.Component;
+import java.io.*;
+import java.util.Vector;
+import java.util.Enumeration;
+import java.net.URL;
+import java.net.MalformedURLException;
+
+import javax.media.j3d.*;
+import javax.vecmath.Color3f;
+import javax.vecmath.Point3d;
+
+
+/**
+ * This class implements the Loader API and allows users to load
+ * Lightwave 3D scene files. In order to load properly, the object
+ * files referred to in the scene files and the image files referred
+ * to by the object files must all be specified with path and filenames
+ * that are valid with respect to the directory in which the application
+ * is being executed.
+ */
+
+public class Lw3dLoader extends TextfileParser implements Loader {
+
+ Vector objectList;
+ Vector lightList;
+ BranchGroup sceneGroupNode;
+ Color3f ambientColor;
+ LwsCamera camera = null;
+ LwsFog fog = null;
+ LwsBackground background = null;
+ int loadFlags = 0;
+ int loadBehaviors = 0;
+ Vector sceneBehaviors;
+ SceneBase scene = null;
+ String basePath = null;
+ String internalBasePath = null;
+ URL baseUrl = null;
+ String internalBaseUrl = null; // store url base as String
+ static final int FILE_TYPE_NONE = 0;
+ static final int FILE_TYPE_URL = 1;
+ static final int FILE_TYPE_FILENAME = 2;
+ static final int FILE_TYPE_READER = 4;
+ int fileType = FILE_TYPE_NONE;
+
+ /**
+ * Default constructor. Sets up default values for some variables.
+ */
+ public Lw3dLoader() {
+
+ ambientColor = new Color3f(0f, 0f, 0f);
+ objectList = new Vector();
+ lightList = new Vector();
+ debugPrinter.setValidOutput(0x0);
+
+ }
+
+ /**
+ * This constructor takes a flags word that specifies which types of
+ * scenefile items should be loaded into the scene. The possible
+ * values are specified in the com.sun.j3d.loaders.Loader class.
+ */
+ public Lw3dLoader(int flags) {
+
+ this();
+ loadFlags = flags;
+ loadBehaviors = (loadFlags & Loader.LOAD_BEHAVIOR_NODES);
+
+ }
+
+ /**
+ * This method loads the named file and returns the Scene
+ * containing the scene. Any data files referenced by the Reader
+ * should be located in the same place as the named file; otherwise,
+ * users should specify an alternate base path with the setBaseUrl(URL)
+ * method.
+ */
+ public Scene load(URL url) throws FileNotFoundException,
+ IncorrectFormatException, ParsingErrorException {
+
+ fileType = FILE_TYPE_URL;
+ setInternalBaseUrl(url);
+ InputStreamReader reader;
+ try {
+ reader = new InputStreamReader(
+ new BufferedInputStream(url.openStream()));
+ }
+ catch (IOException e) {
+ throw new FileNotFoundException(e.getMessage());
+ }
+ Scene returnScene = load(reader);
+ fileType = FILE_TYPE_NONE;
+ return returnScene;
+ }
+
+ /**
+ * This method loads the named file and returns the Scene
+ * containing the scene. Any data files referenced by this
+ * file should be located in the same place as the named file;
+ * otherwise users should specify an alternate base path with
+ * the setBasePath(String) method.
+ */
+ public Scene load(String fileName) throws FileNotFoundException,
+ IncorrectFormatException, ParsingErrorException {
+
+ fileType = FILE_TYPE_FILENAME;
+ setInternalBasePath(fileName);
+ Reader reader = new BufferedReader(new FileReader(fileName));
+ Scene returnScene = load(reader);
+ fileType = FILE_TYPE_NONE;
+ return returnScene;
+ }
+
+ /**
+ * This method loads the Reader and returns the Scene
+ * containing the scene. Any data files referenced by the Reader should
+ * be located in the user's current working directory.
+ */
+ public Scene load(Reader reader) throws FileNotFoundException,
+ IncorrectFormatException, ParsingErrorException {
+
+ if (fileType == FILE_TYPE_NONE)
+ fileType = FILE_TYPE_READER;
+ StreamTokenizer tokenizer = new StreamTokenizer(reader);
+ setupTokenizer(tokenizer);
+
+ getAndCheckString(tokenizer, "LWSC");
+ getNumber(tokenizer);
+ getAndCheckString(tokenizer, "FirstFrame");
+ int firstFrame = (int)getNumber(tokenizer);
+ getAndCheckString(tokenizer, "LastFrame");
+ int finalFrame = (int)getNumber(tokenizer);
+ skipUntilString(tokenizer, "FramesPerSecond");
+ double fps = getNumber(tokenizer);
+ float totalTime = (float)(finalFrame - firstFrame)/(float)fps;
+ boolean done = false;
+ while (!done) {
+ int token;
+ try {
+ token = tokenizer.nextToken();
+ }
+ catch (IOException e) {
+ throw new ParsingErrorException(e.getMessage());
+ }
+ switch (tokenizer.ttype) {
+ case StreamTokenizer.TT_EOF:
+ done = true;
+ break;
+ case StreamTokenizer.TT_WORD:
+ debugOutputLn(VALUES, " String = " + tokenizer.sval);
+ if (tokenizer.sval.equals("AddNullObject")) {
+ LwsObject obj =
+ new LwsObject(tokenizer, false,
+ firstFrame,
+ finalFrame, totalTime,
+ this,
+ debugPrinter.getValidOutput());
+ obj.createJava3dObject(null, loadBehaviors);
+ objectList.addElement(obj);
+ }
+ else if (tokenizer.sval.equals("LoadObject")) {
+ String filename = getString(tokenizer);
+ tokenizer.pushBack(); // push filename token back
+ debugOutputLn(TIME, "loading " + filename + " at " +
+ System.currentTimeMillis());
+ LwsObject obj = new LwsObject(tokenizer, true,
+ firstFrame,
+ finalFrame, totalTime,
+ this,
+ debugPrinter.getValidOutput());
+ debugOutputLn(TIME, "done loading at " +
+ System.currentTimeMillis());
+ LwsObject cloneObject = null;
+ for (Enumeration e = objectList.elements() ;
+ e.hasMoreElements() ;) {
+ LwsObject tmpObj = (LwsObject)e.nextElement();
+ if (tmpObj.fileName != null &&
+ tmpObj.fileName.equals(filename)) {
+ cloneObject = tmpObj;
+ break;
+ }
+ }
+ obj.createJava3dObject(cloneObject, loadBehaviors);
+ objectList.addElement(obj);
+ }
+ else if (tokenizer.sval.equals("AmbientColor")) {
+ ambientColor.x = (float)getNumber(tokenizer)/255f;
+ ambientColor.y = (float)getNumber(tokenizer)/255f;
+ ambientColor.z = (float)getNumber(tokenizer)/255f;
+ }
+ else if (tokenizer.sval.equals("AmbIntensity")) {
+ // TODO: must be able to handle envelopes here
+ float intensity = (float)getNumber(tokenizer);
+ ambientColor.x *= intensity;
+ ambientColor.y *= intensity;
+ ambientColor.z *= intensity;
+ }
+ else if (tokenizer.sval.equals("AddLight")) {
+ LwsLight light =
+ new LwsLight(tokenizer,
+ finalFrame, totalTime,
+ debugPrinter.getValidOutput());
+ light.createJava3dObject(loadBehaviors);
+ lightList.addElement(light);
+ }
+ else if (tokenizer.sval.equals("ShowCamera")) {
+ camera = new LwsCamera(tokenizer, firstFrame,
+ finalFrame, totalTime,
+ debugPrinter.getValidOutput());
+ camera.createJava3dObject(loadBehaviors);
+ }
+ else if (tokenizer.sval.equals("FogType")) {
+ int fogType = (int)getNumber(tokenizer);
+ if (fogType != 0) {
+ fog = new LwsFog(tokenizer,
+ debugPrinter.getValidOutput());
+ fog.createJava3dObject();
+ }
+ }
+ else if (tokenizer.sval.equals("SolidBackdrop")) {
+ background =
+ new LwsBackground(tokenizer,
+ debugPrinter.getValidOutput());
+ background.createJava3dObject();
+ }
+ break;
+ default:
+ debugOutputLn(VALUES, " Unknown ttype, token = " +
+ tokenizer.ttype + ", " + token);
+ break;
+ }
+ }
+
+ // Set up scene groups and parent objects appropriately
+ sceneGroupNode = new BranchGroup();
+ sceneBehaviors = new Vector();
+ parentObjects();
+ constructScene();
+
+ return scene;
+
+ }
+
+
+ /**
+ * This method creates the Scene (actually SceneBase) data structure
+ * and adds all appropriate items to it. This is the data structure
+ * that the user will get back from the load() call and inquire to
+ * get data from the scene.
+ */
+ void constructScene() {
+
+ // Construct Scene data structure
+ scene = new SceneBase();
+
+ if ((loadFlags & Loader.LOAD_LIGHT_NODES) != 0) {
+ addLights();
+ addAmbient();
+ }
+
+ if ((loadFlags & Loader.LOAD_FOG_NODES) != 0)
+ addFog();
+
+ if ((loadFlags & Loader.LOAD_BACKGROUND_NODES) != 0)
+ addBackground();
+
+ if ((loadFlags & Loader.LOAD_VIEW_GROUPS) != 0)
+ addCamera();
+
+ if (loadBehaviors != 0)
+ addBehaviors();
+
+ scene.setSceneGroup(sceneGroupNode);
+
+ // now add named objects to the scenes name table
+ for (Enumeration e = objectList.elements(); e.hasMoreElements() ;) {
+
+ LwsObject obj = (LwsObject)e.nextElement();
+ if (obj.fileName != null)
+ scene.addNamedObject(obj.fileName,(Object)obj.getObjectNode());
+ else if (obj.objName != null)
+ scene.addNamedObject(obj.objName,(Object)obj.getObjectNode());
+
+ }
+ }
+
+
+ /**
+ * Creates a url for internal use. This method is not currently
+ * used (url's are ignored for this loader; only filenames work)
+ */
+ void setInternalBaseUrl(URL url) {
+// System.out.println("setInternalBaseUrl url = " + url);
+ java.util.StringTokenizer stok =
+ new java.util.StringTokenizer(url.toString(),
+// java.io.File.separator);
+ "\\/");
+ int tocount = stok.countTokens()-1;
+ StringBuffer sb = new StringBuffer(80);
+ for(int ji = 0; ji < tocount ; ji++) {
+ String a = stok.nextToken();
+ if((ji == 0) && //(!a.equals("file:"))) {
+ (!a.regionMatches(true, 0, "file:", 0, 5))) {
+ sb.append(a);
+ // urls use / on windows also
+// sb.append(java.io.File.separator);
+// sb.append(java.io.File.separator);
+ sb.append('/');
+ sb.append('/');
+ } else {
+ sb.append(a);
+ // urls use / on windows also
+// sb.append( java.io.File.separator );
+ sb.append('/');
+ }
+ }
+ internalBaseUrl = sb.toString();
+// System.out.println("internalBaseUrl = " + internalBaseUrl);
+ }
+
+ /**
+ * Standardizes the filename for use in the loader
+ */
+ void setInternalBasePath(String fileName) {
+ java.util.StringTokenizer stok =
+ new java.util.StringTokenizer(fileName,
+ java.io.File.separator);
+ int tocount = stok.countTokens()-1;
+ StringBuffer sb = new StringBuffer(80);
+ if (fileName!= null &&
+ fileName.startsWith(java.io.File.separator))
+ sb.append(java.io.File.separator);
+ for(int ji = 0; ji < tocount ; ji++) {
+ String a = stok.nextToken();
+ sb.append(a);
+ sb.append( java.io.File.separator );
+ }
+ internalBasePath = sb.toString();
+ }
+
+ String getInternalBasePath() {
+ return internalBasePath;
+ }
+
+ String getInternalBaseUrl() {
+ return internalBaseUrl;
+ }
+
+ int getFileType() {
+ return fileType;
+ }
+
+ /**
+ * This method parents all objects in the scene appropriately. If
+ * the scen file specifies a Parent node for the object, then the
+ * object is parented to that node. If not, then the object is
+ * parented to the scene's root.
+ */
+ void parentObjects() {
+ debugOutputLn(TRACE, "parentObjects()");
+ for (Enumeration e = objectList.elements(); e.hasMoreElements(); ) {
+
+ LwsObject obj = (LwsObject)e.nextElement();
+ if (obj.getParent() != -1) {
+
+ LwsObject parent = (LwsObject)
+ objectList.elementAt(obj.getParent() - 1);
+ parent.addChild(obj);
+ debugOutputLn(VALUES, "added child successfully");
+
+ } else {
+
+ if (obj.getObjectNode() != null)
+ sceneGroupNode.addChild(obj.getObjectNode());
+
+ }
+
+ // Collect all behaviors
+ if (loadBehaviors != 0) {
+ if (!(obj.getObjectBehaviors()).isEmpty()) {
+ sceneBehaviors.addAll(obj.getObjectBehaviors());
+
+ }
+ }
+ }
+
+ debugOutputLn(LINE_TRACE, "Done with parentObjects()");
+
+ }
+
+
+ /**
+ * This method sets the base URL name for data files
+ * associated with the file passed into the load(URL) method.
+ * The basePath should be null by default, which is an
+ * indicator to the loader that it should look for any
+ * associated files starting from the same directory as the
+ * file passed into the load(URL) method.
+ */
+ public void setBaseUrl(URL url) {
+ baseUrl = url;
+ }
+
+ /**
+ * This method sets the base path to be used when searching for all
+ * data files within a Lightwave scene.
+ */
+ public void setBasePath(String pathName) {
+ // This routine standardizes path names so that all pathnames
+ // will have standard file separators, they'll end in a separator
+ // character, and if the user passes in null or "" (meaning to
+ // set the current directory as the base path), this will become
+ // "./" (or ".\")
+ 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;
+ }
+
+ /**
+ * Returns the current base URL setting.
+ */
+ public URL getBaseUrl() {
+ return baseUrl;
+ }
+
+ /**
+ * Returns the current base path setting.
+ */
+ public String getBasePath() {
+ return basePath;
+ }
+
+ /**
+ * This method sets the load flags for the file. The flags should
+ * equal 0 by default (which tells the loader to only load geometry).
+ */
+ public void setFlags(int flags) {
+ loadFlags = flags;
+ }
+
+ /**
+ * Returns the current loading flags setting.
+ */
+ public int getFlags() {
+ return loadFlags;
+ }
+
+
+
+ /**
+ * getObject() iterates through the objectList checking the given
+ * name against the fileName and objectName of each object in turn.
+ * For the filename, it carves off the pathname and just checks the
+ * final name (e.g., "missile.lwo").
+ * If name has []'s at the end, it will use the number inside those
+ * brackets to pick which object out of an ordered set it will
+ * send back (objectList is created in the order that objects
+ * exist in the file, so this order should correspond to the order
+ * specified by the user). If no []'s exist, just pass back the
+ * first one encountered that matches.
+ */
+ public TransformGroup getObject(String name) {
+ debugOutputLn(TRACE, "getObject()");
+ int indexNumber = -1;
+ int currentObjectCount = 0;
+ String subobjectName = name;
+ if (name.indexOf("[") != -1) {
+ // caller wants specifically numbered subjbect; get that number
+ int bracketsIndex = name.indexOf("[");
+ subobjectName = name.substring(0, bracketsIndex);
+ String bracketsString = name.substring(bracketsIndex);
+ int bracketEndIndex = bracketsString.indexOf("]");
+ String indexString = bracketsString.substring(1, bracketEndIndex);
+ indexNumber = (new Integer(indexString)).intValue();
+ }
+ for (Enumeration e = objectList.elements() ;
+ e.hasMoreElements() ;) {
+ LwsObject tempObj = (LwsObject)e.nextElement();
+ debugOutputLn(VALUES, "tempObj, file, objname = " +
+ tempObj + tempObj.fileName +
+ tempObj.objName);
+ if ((tempObj.fileName != null &&
+ tempObj.fileName.indexOf(subobjectName) != -1) ||
+ (tempObj.objName != null &&
+ tempObj.objName.indexOf(subobjectName) != -1)) {
+ if (indexNumber < 0 ||
+ indexNumber == currentObjectCount)
+ return tempObj.getObjectNode();
+ else
+ currentObjectCount++;
+ }
+ }
+ debugOutputLn(VALUES, " no luck - wanted " +
+ name + " returning null");
+ return null;
+ }
+
+
+ /**
+ * This method sets up the StreamTokenizer for the scene file. Note
+ * that we're not parsing numbers as numbers because the tokenizer
+ * does not interpret scientific notation correctly.
+ */
+ void setupTokenizer(StreamTokenizer tokenizer) {
+ tokenizer.resetSyntax();
+ tokenizer.wordChars('a', 'z');
+ tokenizer.wordChars('A', 'Z');
+ tokenizer.wordChars(128 + 32, 255);
+ tokenizer.whitespaceChars(0, ' ');
+ tokenizer.commentChar('/');
+ tokenizer.quoteChar('"');
+ tokenizer.quoteChar('\'');
+ tokenizer.wordChars('0', '9');
+ tokenizer.wordChars('.', '.');
+ tokenizer.wordChars('-', '-');
+ tokenizer.wordChars('/', '/');
+ tokenizer.wordChars('\\', '\\');
+ tokenizer.wordChars('_', '_');
+ tokenizer.wordChars('&', '&');
+ tokenizer.ordinaryChar('(');
+ tokenizer.ordinaryChar(')');
+ tokenizer.whitespaceChars('\r', '\r');
+
+ // add ':' as wordchar so urls will work
+ tokenizer.wordChars(':', ':');
+ // add '~' as wordchar for urls
+ tokenizer.wordChars('~', '~');
+ }
+
+ /**
+ * Adds Ambient lighting effects to the scene
+ */
+ void addAmbient() {
+ AmbientLight aLgt = new AmbientLight(ambientColor);
+ BoundingSphere bounds =
+ new BoundingSphere(new Point3d(0.0,0.0,0.0), 100000.0);
+ aLgt.setInfluencingBounds(bounds);
+ sceneGroupNode.addChild(aLgt);
+ // scope ambient light to the lw3d scene
+ aLgt.addScope(sceneGroupNode);
+ scene.addLightNode(aLgt);
+ }
+
+ /**
+ * Add any defined lights to the java3d scene
+ */
+ void addLights() {
+ // Add lights to the scene
+ for (Enumeration e1 = lightList.elements(); e1.hasMoreElements(); ) {
+
+ debugOutputLn(LINE_TRACE, "adding light to scene group");
+ LwsLight light = (LwsLight)e1.nextElement();
+
+ if (light.getObjectNode() != null) {
+ // scope light to the lw3d scene
+ light.getLight().addScope(sceneGroupNode);
+
+ if (light.getParent() != -1) {
+ LwsObject parent = (LwsObject)
+ objectList.elementAt(light.getParent() - 1);
+ parent.addChild(light);
+ }
+ else { // No parent - add to scene group
+ sceneGroupNode.addChild(light.getObjectNode());
+
+ }
+
+ // collect behaviors if LOAD_BEHAVIOR_NODES is set
+ if (loadBehaviors != 0) {
+ if (!(light.getObjectBehaviors()).isEmpty())
+ sceneBehaviors.addAll(light.getObjectBehaviors());
+
+ }
+
+ scene.addLightNode(light.getLight());
+ }
+ else
+ debugOutputLn(LINE_TRACE, "light object null?");
+ }
+ }
+
+ /**
+ * Adds the Camera's transform group to the scene, either by parenting
+ * it to the appropriate object or by adding it to the scene root.
+ * To use this camera data, users can request the camera/view data
+ * for the scene and can then insert a ViewPlatform in the transform group.
+ */
+ void addCamera() {
+ // Add camera effects to scene.
+ if (camera != null) {
+ if (camera.getParent() != -1) {
+ debugOutputLn(VALUES, "camera parent = " +
+ camera.getParent());
+ LwsObject parent = (LwsObject)
+ objectList.elementAt(camera.getParent() - 1);
+ parent.addChild(camera);
+ debugOutputLn(VALUES, "added child successfully");
+ }
+ else {
+ sceneGroupNode.addChild(camera.getObjectNode());
+
+ }
+
+ // collect behaviors if LOAD_BEHAVIOR_NODES is set
+ if (loadBehaviors != 0) {
+ if (!(camera.getObjectBehaviors()).isEmpty())
+ sceneBehaviors.addAll(camera.getObjectBehaviors());
+ }
+
+ scene.addViewGroup(camera.getObjectNode());
+ }
+ }
+
+ /**
+ * Add appropriate fog effects to the scene
+ */
+ void addFog() {
+ if (fog != null) {
+ Fog fogNode = fog.getObjectNode();
+ if (fogNode != null) {
+ sceneGroupNode.addChild(fogNode);
+ scene.addFogNode(fogNode);
+ }
+ }
+ }
+
+ /**
+ * Add the behaviors to the scene
+ */
+ void addBehaviors() {
+ if (!sceneBehaviors.isEmpty()) {
+ Enumeration e = sceneBehaviors.elements();
+ while (e.hasMoreElements()) {
+ scene.addBehaviorNode((Behavior)e.nextElement());
+ }
+ }
+ }
+
+ /**
+ * Add appropriate background effects to the scene. Note that the java3d
+ * background may not have all of the information of the lw3d background,
+ * as the loader does not currently process items such as gradients between
+ * the horizon and sky colors
+ */
+ void addBackground() {
+ if (background != null) {
+ Background bgNode = background.getObjectNode();
+ if (bgNode != null) {
+ sceneGroupNode.addChild(bgNode);
+ scene.addBackgroundNode(bgNode);
+ }
+ }
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/LwLightObject.java b/src/classes/share/com/sun/j3d/loaders/lw3d/LwLightObject.java
new file mode 100644
index 0000000..ce61df2
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/LwLightObject.java
@@ -0,0 +1,100 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+
+
+import javax.vecmath.Color3f;
+import javax.media.j3d.Light;
+
+
+/**
+ * This class is used to set the Light Intensity value according to the
+ * keyframe value of the envelope for that light. The class is used in
+ * conjunction with LightIntensityPathInterpolator, which uses this
+ * class as the target of its interpolations.
+ */
+
+class LwLightObject {
+
+ float intensity;
+ Color3f color;
+ Light theLight;
+
+ LwLightObject(Light theLight, float intensity, Color3f color) {
+ this.intensity = intensity;
+ this.color = color;
+ this.theLight = theLight;
+ }
+
+ void setIntensity(float intensity) {
+ Color3f newLightColor = new Color3f(color.x * intensity,
+ color.y * intensity,
+ color.z * intensity);
+ if (theLight != null)
+ theLight.setColor(newLightColor);
+ this.intensity = intensity;
+ }
+
+ void setColor(Color3f color) {
+ Color3f newLightColor = new Color3f(color.x * intensity,
+ color.y * intensity,
+ color.z * intensity);
+ if (theLight != null)
+ theLight.setColor(newLightColor);
+ this.color = color;
+ }
+
+ void setLight(Light l) {
+ theLight = l;
+ Color3f newLightColor = new Color3f(color.x * intensity,
+ color.y * intensity,
+ color.z * intensity);
+ if (theLight != null)
+ theLight.setColor(newLightColor);
+ }
+}
+
+
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/LwoParser.java b/src/classes/share/com/sun/j3d/loaders/lw3d/LwoParser.java
new file mode 100644
index 0000000..40f2df0
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/LwoParser.java
@@ -0,0 +1,424 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.util.Vector;
+import java.util.Date;
+import java.util.Enumeration;
+import com.sun.j3d.loaders.lw3d.LWOBFileReader;
+import com.sun.j3d.internal.J3dUtilsI18N;
+import java.net.*;
+import com.sun.j3d.loaders.ParsingErrorException;
+import com.sun.j3d.loaders.IncorrectFormatException;
+
+
+/**
+ * This class is responsible for parsing a binary Object file and storing
+ * the data. This class is not called directly, but is the parent class of
+ * J3dLwoObject. The subclass calls this class to parse the file, then it
+ * turns the resulting data into Java3D objects. LwoObject instantiates
+ * an LWOBFileReader object to parse the data. Then the class creates a
+ * list of ShapeHolder objects to hold all of the vertex/facet data and
+ * surface references and creates a list of LwoSurface objects to hold
+ * the data for each surface.<BR>
+ * Rather than describe in detail how the file is parsed for each method,
+ * I advise the user of this code to understand the lw3d file format
+ * specs, which are pretty clear.
+ */
+
+class LwoParser extends ParserObject {
+
+ LWOBFileReader theReader;
+ int currLength;
+ float coordsArray[];
+ float normalCoordsArray[];
+ int facetIndicesArray[];
+ int facetSizesArray[];
+ int normalIndicesArray[];
+ int red = 255, green = 255, blue = 255;
+ float diffuse = 0.0f, specular = 0.0f, transparency = 0.0f, luminosity = 0.0f;
+ int gloss = 128;
+ Vector surfNameList = null;
+ Vector surfaceList = new Vector(200);
+ Vector shapeList = new Vector(200);
+
+ /**
+ * Constructor: Creates file reader and calls parseFile() to actually
+ * read the file and grab the data
+ */
+ LwoParser(String fileName, int debugVals)
+ throws FileNotFoundException {
+
+ super(debugVals);
+ debugOutputLn(TRACE, "parser()");
+ long start = System.currentTimeMillis();
+ theReader = new LWOBFileReader(fileName);
+ debugOutputLn(TIME, " file opened in " +
+ (System.currentTimeMillis() - start));
+ parseFile();
+ }
+
+ LwoParser(URL url, int debugVals)
+ throws FileNotFoundException {
+ super(debugVals);
+ debugOutputLn(TRACE, "parser()");
+ try {
+ long start = System.currentTimeMillis();
+ theReader = new LWOBFileReader(url);
+ debugOutputLn(TIME, " file opened in " +
+ (System.currentTimeMillis() - start));
+ }
+ catch (IOException ex) {
+ throw new FileNotFoundException(url.toString());
+ }
+ parseFile();
+ }
+
+
+ /**
+ * Detail polygons are currently not implemented by this loader. Their
+ * structure in geometry files is a bit complex, so there's this separate
+ * method for simply parsing through and ignoring the data for detail
+ * polygons
+ */
+ int skipDetailPolygons(int numPolys) throws ParsingErrorException {
+ debugOutputLn(TRACE, "skipDetailPolygons(), numPolys = " + numPolys);
+ int lengthRead = 0;
+ int vert;
+
+ try {
+ for (int polyNum = 0; polyNum < numPolys; ++polyNum) {
+ debugOutputLn(VALUES, "polyNum = " + polyNum);
+ int numVerts = theReader.getShortInt();
+ theReader.skip(numVerts * 2 + 2); // skip indices plus surf
+ lengthRead += (numVerts * 2) + 4; // increment counter
+ }
+ }
+ catch (IOException e) {
+ debugOutputLn(EXCEPTION, "Exception in reading detail polys: " + e);
+ throw new ParsingErrorException(e.getMessage());
+ }
+ return lengthRead;
+ }
+
+ /**
+ * Returns already-existing ShapeHolder if one exists with the same
+ * surface and the same geometry type (point, line, or poly)
+ */
+ ShapeHolder getAppropriateShape(int numSurf, int numVerts) {
+ for (Enumeration e = shapeList.elements();
+ e.hasMoreElements() ;) {
+ ShapeHolder shape = (ShapeHolder)e.nextElement();
+ if (shape.numSurf == numSurf)
+ if (shape.numVerts == numVerts ||
+ (shape.numVerts > 3 &&
+ numVerts > 3))
+ return shape;
+ }
+ return null;
+ }
+
+
+ /**
+ * Parse the file for all the data for a POLS object (polygon
+ * description)
+ */
+ void getPols(int length) {
+ debugOutputLn(TRACE, "getPols(len), len = " + length);
+ int vert;
+ int lengthRead = 0;
+ int prevNumVerts = -1;
+ int prevNumSurf = 0;
+ Vector facetSizesList;
+ int facetIndicesArray[];
+ facetSizesList =
+ new Vector(length/6); // worst case size (every poly one vert)
+ // Note that our array sizes are hardcoded because we don't
+ // know until we're done how large they will be
+ facetIndicesArray = new int[length/2];
+ ShapeHolder shape = new ShapeHolder(debugPrinter.getValidOutput());
+ debugOutputLn(VALUES, "new shape = " + shape);
+ shape.coordsArray = coordsArray;
+ shape.facetSizesList = facetSizesList;
+ //shape.facetIndicesList = facetIndicesList;
+ shape.facetIndicesArray = facetIndicesArray;
+ shapeList.addElement(shape);
+
+ //long startTime = (new Date()).getTime();
+ boolean firstTime = true;
+ while (lengthRead < length) {
+ int numVerts = theReader.getShortInt();
+ lengthRead += 2;
+ int intArray[] = new int[numVerts];
+ for (int i = 0; i < numVerts; ++i) {
+ intArray[i] = theReader.getShortInt();
+ lengthRead += 2;
+ }
+
+ int numSurf = theReader.getShortInt();
+ lengthRead += 2;
+ long startTimeBuff = 0, startTimeList = 0;
+ if (!firstTime &&
+ (numSurf != prevNumSurf ||
+ ((numVerts != prevNumVerts) &&
+ ((prevNumVerts < 3) ||
+ (numVerts < 3))))) {
+ // If above true, then start new shape
+ shape = getAppropriateShape(numSurf, numVerts);
+ if (shape == null) {
+ //debugOutputLn(LINE_TRACE, "Starting new shape");
+ facetSizesList = new Vector(length/6);
+ facetIndicesArray = new int[length/2];
+ shape = new ShapeHolder(debugPrinter.getValidOutput());
+ shape.coordsArray = coordsArray;
+ shape.facetSizesList = facetSizesList;
+ //shape.facetIndicesList = facetIndicesList;
+ shape.facetIndicesArray = facetIndicesArray;
+ shape.numSurf = numSurf;
+ shape.numVerts = numVerts;
+ shapeList.addElement(shape);
+ }
+ else {
+ facetSizesList = shape.facetSizesList;
+ facetIndicesArray = shape.facetIndicesArray;
+ }
+ }
+ else {
+ shape.numSurf = numSurf;
+ shape.numVerts = numVerts;
+ }
+ prevNumVerts = numVerts;
+ prevNumSurf = numSurf;
+ facetSizesList.addElement(new Integer(numVerts));
+
+ int currPtr = 0;
+ System.arraycopy(intArray, 0,
+ facetIndicesArray, shape.currentNumIndices,
+ numVerts);
+ shape.currentNumIndices += numVerts;
+ if (numSurf < 0) { // neg number means detail poly
+ int numPolys = theReader.getShortInt();
+ lengthRead += skipDetailPolygons(numPolys);
+ shape.numSurf = ~shape.numSurf & 0xffff;
+ if (shape.numSurf == 0)
+ shape.numSurf = 1; // Can't have surface = 0
+ }
+ firstTime = false;
+ }
+ }
+
+ /**
+ * Parses file to get the names of all surfaces. Each polygon will
+ * be associated with a particular surface number, which is the index
+ * number of these names
+ */
+ void getSrfs(int length) {
+ String surfName = new String();
+ surfNameList = new Vector(length/2); // worst case size (each name 2 chars long)
+ int lengthRead = 0;
+ int stopMarker = theReader.getMarker() + length;
+
+ int surfIndex = 0;
+ while (theReader.getMarker() < stopMarker) {
+ debugOutputLn(VALUES, "marker, stop = " +
+ theReader.getMarker() + ", " + stopMarker);
+ debugOutputLn(LINE_TRACE, "About to call getString");
+ surfName = theReader.getString();
+ debugOutputLn(VALUES, "Surfname = " + surfName);
+ surfNameList.addElement(surfName);
+ }
+ }
+
+ /**
+ * Parses file to get all vertices
+ */
+ void getPnts(int length) throws ParsingErrorException {
+ int numVerts = length / 12;
+ float x, y, z;
+
+ coordsArray = new float[numVerts*3];
+ theReader.getVerts(coordsArray, numVerts);
+ }
+
+ /**
+ * Creates new LwoSurface object that parses file and gets all
+ * surface parameters for a particular surface
+ */
+ void getSurf(int length) throws FileNotFoundException {
+ debugOutputLn(TRACE, "getSurf()");
+
+ // Create LwoSurface object to read and hold each surface, then
+ // store that surface in a vector of all surfaces.
+
+ LwoSurface surf = new LwoSurface(theReader, length,
+ debugPrinter.getValidOutput());
+ surfaceList.addElement(surf);
+ }
+
+
+ /**
+ * parses entire file.
+ * return -1 on error or 0 on completion
+ */
+ int parseFile() throws FileNotFoundException, IncorrectFormatException {
+ debugOutputLn(TRACE, "parseFile()");
+ int length = 0;
+ int lengthRead = 0;
+ int fileLength = 100000;
+
+ long loopStartTime = System.currentTimeMillis();
+ // Every parsing unit begins with a four character string
+ String tokenString = theReader.getToken();
+
+ while (!(tokenString == null) &&
+ lengthRead < fileLength) {
+ long startTime = System.currentTimeMillis();
+ // Based on value of tokenString, go to correct parsing method
+ length = theReader.getInt();
+
+ lengthRead += 4;
+ //debugOutputLn(VALUES, "length, lengthRead, fileLength = " +
+ // length + ", " + lengthRead + ", " + fileLength);
+ //debugOutputLn(VALUES, "LWOB marker is at: " + theReader.getMarker());
+
+ if (tokenString.equals("FORM")) {
+ //debugOutputLn(LINE_TRACE, "got a form");
+ fileLength = length + 4;
+ length = 0;
+ tokenString = theReader.getToken();
+ lengthRead += 4;
+ if (!tokenString.equals("LWOB"))
+ throw new IncorrectFormatException(
+ "File not of FORM-length-LWOB format");
+ }
+ else if (tokenString.equals("PNTS")) {
+ //debugOutputLn(LINE_TRACE, "PNTS");
+ getPnts(length);
+ debugOutputLn(TIME, "done with " + tokenString + " in " +
+ (System.currentTimeMillis() - startTime));
+ }
+ else if (tokenString.equals("POLS")) {
+ //debugOutputLn(LINE_TRACE, "POLS");
+ getPols(length);
+ debugOutputLn(TIME, "done with " + tokenString + " in " +
+ (System.currentTimeMillis() - startTime));
+ }
+ else if (tokenString.equals("SRFS")) {
+ //debugOutputLn(LINE_TRACE, "SRFS");
+ getSrfs(length);
+ debugOutputLn(TIME, "done with " + tokenString + " in " +
+ (System.currentTimeMillis() - startTime));
+ }
+ else if (tokenString.equals("CRVS")) {
+ //debugOutputLn(LINE_TRACE, "CRVS");
+ theReader.skipLength(length);
+ //debugOutputLn(TIME, "done with " + tokenString + " in " +
+ // (System.currentTimeMillis() - startTime));
+ }
+ else if (tokenString.equals("PCHS")) {
+ //debugOutputLn(LINE_TRACE, "PCHS");
+ theReader.skipLength(length);
+ //debugOutputLn(TIME, "done with " + tokenString + " in " +
+ // (System.currentTimeMillis() - startTime));
+ }
+ else if (tokenString.equals("SURF")) {
+ //debugOutputLn(LINE_TRACE, "SURF");
+ getSurf(length);
+ //debugOutputLn(VALUES, "Done with SURF, marker = " + theReader.getMarker());
+ debugOutputLn(TIME, "done with " + tokenString + " in " +
+ (System.currentTimeMillis() - startTime));
+ }
+ else if (tokenString.equals("LWOB")) {
+ //debugOutputLn(LINE_TRACE, "LWOB");
+ }
+ else {
+ //debugOutputLn(LINE_TRACE, "Unknown object = " + tokenString);
+ theReader.skipLength(length);
+ //debugOutputLn(TIME, "done with " + tokenString + " in " +
+ // (System.currentTimeMillis() - startTime));
+ }
+ lengthRead += length;
+ if (lengthRead < fileLength) {
+ //debugOutputLn(VALUES, "end of parseFile, length, lengthRead = " +
+ // length + ", " + lengthRead);
+ tokenString = theReader.getToken();
+ lengthRead += 4;
+ //debugOutputLn(VALUES, "just got tokenString = " + tokenString);
+ }
+ }
+ debugOutputLn(TIME, "done with parseFile in " +
+ (System.currentTimeMillis() - loopStartTime));
+ return 0;
+ }
+
+ /**
+ * This method is used only for testing
+ */
+ static void main(String[] args) {
+ String fileName;
+ if (args.length == 0)
+ fileName = "cube.obj";
+ else
+ fileName = args[0];
+
+ try {
+ LwoParser theParser = new LwoParser(fileName, 0);
+ }
+ catch (FileNotFoundException e) {
+ System.err.println(e.getMessage());
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+}
+
+
+
+
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/LwoSurface.java b/src/classes/share/com/sun/j3d/loaders/lw3d/LwoSurface.java
new file mode 100644
index 0000000..813b5ad
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/LwoSurface.java
@@ -0,0 +1,316 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+import java.awt.Image;
+import java.io.IOException;
+import java.util.Vector;
+import java.util.Enumeration;
+import javax.vecmath.Color3f;
+import javax.vecmath.Vector3f;
+import com.sun.j3d.loaders.lw3d.LWOBFileReader;
+import com.sun.j3d.internal.J3dUtilsI18N;
+import java.io.FileNotFoundException;
+import com.sun.j3d.loaders.IncorrectFormatException;
+import com.sun.j3d.loaders.ParsingErrorException;
+
+
+/**
+ * This class is responsible for retrieving the surface parameters for a
+ * particular surface from a binary Object file and turning that data
+ * into Java3D data. These surface parameters include
+ * diffuse/specular/emissive properties, color, shininess, transparency,
+ * and textures. For textures, this class instantiates a LwoTexture object
+ * to parse that data and turn it into Java3D texture data.
+ */
+
+class LwoSurface extends ParserObject {
+
+ LWOBFileReader theReader;
+ int red = 255, green = 255, blue = 255;
+ float diffuse = 0.0f, specular = 0.0f, transparency = 0.0f, luminosity = 0.0f;
+ float creaseAngle = 0.0f;
+ int gloss = 128;
+ Color3f color, diffuseColor, specularColor, emissiveColor;
+ float shininess;
+ Image theImage = null;
+ Vector3f textureCenter = null, textureSize = null;
+ int textureAxis;
+ String surfName;
+ Vector textureList = new Vector();
+
+ /**
+ * Constructor that parses surface data from the binary file
+ * and creates the necessary Java3d objects
+ */
+ LwoSurface(LWOBFileReader reader, int length, int debugVals)
+ throws FileNotFoundException {
+
+ super(debugVals);
+ debugOutputLn(TRACE, "LwoSurface()");
+ theReader = reader;
+ getSurf(length);
+ setJ3dColors();
+ }
+
+ /**
+ * Creates Java3d color objects from the lw3d surface data
+ */
+ void setJ3dColors() {
+ color = new Color3f((float)red/(float)255,
+ (float)green/(float)255,
+ (float)blue/(float)255);
+ diffuseColor = new Color3f(diffuse*color.x,
+ diffuse*color.y,
+ diffuse*color.z);
+ specularColor = new Color3f(specular*color.x,
+ specular*color.y,
+ specular*color.z);
+ emissiveColor = new Color3f(luminosity*color.x,
+ luminosity*color.y,
+ luminosity*color.z);
+ shininess = (float)(128.0 * ((float)gloss/1024.0));
+ }
+
+ Color3f getColor() {
+ return color;
+ }
+
+ Color3f getDiffuseColor() {
+ return diffuseColor;
+ }
+
+ Color3f getSpecularColor() {
+ return specularColor;
+ }
+
+ Color3f getEmissiveColor() {
+ return emissiveColor;
+ }
+
+ float getShininess() {
+ return shininess;
+ }
+
+ float getCreaseAngle() {
+ return creaseAngle;
+ }
+
+ /**
+ * Returns the LwoTexture for the surface, if any is defined. Note that
+ * lw3d allows users to define multiple textures for any surface, which
+ * is not possible through Java3d. Therefore, we just grab the first
+ * texture in any list of textures for a surface
+ */
+ LwoTexture getTexture() {
+ debugOutputLn(TRACE, "getTexture()");
+ try {
+ if (textureList.isEmpty()) {
+ return null;
+ }
+ else {
+ return (LwoTexture)textureList.elementAt(0);
+ }
+ }
+ catch (ArrayIndexOutOfBoundsException e) {
+ debugOutputLn(EXCEPTION,
+ "getTexture(), exception returning first element: " + e);
+ return null;
+ }
+ }
+
+ String getSurfName() {
+ return surfName;
+ }
+
+ float getTransparency() {
+ return transparency;
+ }
+
+ /**
+ * Parses the binary file and gets all data for this surface
+ */
+ void getSurf(int length)
+ throws FileNotFoundException, IncorrectFormatException,
+ ParsingErrorException {
+
+ debugOutputLn(TRACE, "getSurf()");
+
+ // These "got*" booleans are to help use read the best version of
+ // the data - the float values for these parameters should take
+ // precedence over the other format
+ boolean gotLuminosityFloat = false;
+ boolean gotTransparencyFloat = false;
+ boolean gotDiffuseFloat = false;
+ boolean gotSpecularFloat = false;
+ int surfStopMarker = theReader.getMarker() + length;
+ surfName = theReader.getString();
+ String tokenString = theReader.getToken();
+ while (!(tokenString == null) &&
+ theReader.getMarker() < surfStopMarker) {
+ debugOutputLn(VALUES, " tokenString = " + tokenString);
+ debugOutputLn(VALUES, " marker, stop = " +
+ theReader.getMarker() + ", " + surfStopMarker);
+ String textureToken = null;
+ int fieldLength = theReader.getShortInt();
+ debugOutputLn(VALUES, " fl = " + fieldLength);
+
+ if (tokenString.equals("COLR")) {
+ debugOutputLn(LINE_TRACE, " COLR");
+ try {
+ red = theReader.read();
+ green = theReader.read();
+ blue = theReader.read();
+ theReader.read();
+ }
+ catch (IOException e) {
+ throw new ParsingErrorException(e.getMessage());
+ }
+ if (fieldLength != 4)
+ throw new IncorrectFormatException(
+ J3dUtilsI18N.getString("LwoSurface0"));
+ }
+ else if (tokenString.equals("FLAG")) {
+ debugOutputLn(LINE_TRACE, " FLAG");
+ theReader.skipLength(fieldLength);
+ }
+ else if (tokenString.equals("VLUM")) {
+ debugOutputLn(LINE_TRACE, " VLUM");
+ luminosity = theReader.getFloat();
+ gotLuminosityFloat = true;
+ }
+ else if (tokenString.equals("LUMI")) {
+ debugOutputLn(LINE_TRACE, " LUMI");
+ if (gotLuminosityFloat)
+ theReader.skipLength(fieldLength);
+ else
+ luminosity = (float)(theReader.getShortInt())/255;
+ }
+ else if (tokenString.equals("VDIF")) {
+ debugOutputLn(LINE_TRACE, " VDIF");
+ if (fieldLength != 4)
+ throw new IncorrectFormatException("VDIF problem");
+ diffuse = theReader.getFloat();
+ gotDiffuseFloat = true;
+ debugOutputLn(VALUES, "diff = " + diffuse);
+ }
+ else if (tokenString.equals("DIFF")) {
+ debugOutputLn(LINE_TRACE, " DIFF");
+ if (gotDiffuseFloat)
+ theReader.skipLength(fieldLength);
+ else
+ diffuse = (float)theReader.getShortInt()/255;
+ }
+ else if (tokenString.equals("VTRN")) {
+ debugOutputLn(LINE_TRACE, " VTRN");
+ transparency = theReader.getFloat();
+ gotTransparencyFloat = true;
+ }
+ else if (tokenString.equals("TRAN")) {
+ debugOutputLn(LINE_TRACE, " TRAN");
+ if (gotTransparencyFloat)
+ theReader.skipLength(fieldLength);
+ else
+ transparency = (float)theReader.getShortInt()/255;
+ }
+ else if (tokenString.equals("VSPC")) {
+ debugOutputLn(LINE_TRACE, " VSPC");
+ specular = theReader.getFloat();
+ gotSpecularFloat = true;
+ debugOutputLn(VALUES, "spec = " + specular);
+ }
+ else if (tokenString.equals("SPEC")) {
+ debugOutputLn(LINE_TRACE, " SPEC");
+ if (gotSpecularFloat)
+ theReader.skipLength(fieldLength);
+ else {
+ if (fieldLength == 4) // Bug in some LW versions
+ specular = (float)theReader.getInt()/255;
+ else
+ specular = (float)theReader.getShortInt()/255;
+ }
+ }
+ else if (tokenString.equals("GLOS")) {
+ debugOutputLn(LINE_TRACE, " GLOS");
+ if (fieldLength == 4)
+ gloss = theReader.getInt();
+ else
+ gloss = theReader.getShortInt();
+ }
+ else if (tokenString.equals("SMAN")) {
+ debugOutputLn(LINE_TRACE, " SMAN");
+ creaseAngle = theReader.getFloat();
+ }
+ else if (tokenString.endsWith("TEX")) {
+ // Textures are complex - hand off this bit to the
+ // LwoTexture class
+ LwoTexture texture =
+ new LwoTexture(theReader,
+ surfStopMarker - theReader.getMarker(),
+ tokenString,
+ debugPrinter.getValidOutput());
+ textureToken = texture.getNextToken();
+ if (texture.isHandled())
+ textureList.addElement(texture);
+ debugOutputLn(WARNING, "val = " + tokenString);
+ }
+ else {
+ debugOutputLn(WARNING,
+ "unrecognized token: " + tokenString);
+ theReader.skipLength(fieldLength);
+ }
+ if (theReader.getMarker() < surfStopMarker) {
+ if (textureToken == null)
+ tokenString = theReader.getToken();
+ else
+ tokenString = textureToken;
+ }
+ }
+ }
+
+}
+
+
+
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/LwoTexture.java b/src/classes/share/com/sun/j3d/loaders/lw3d/LwoTexture.java
new file mode 100644
index 0000000..57bbf7b
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/LwoTexture.java
@@ -0,0 +1,284 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+import java.awt.Component;
+import java.awt.Image;
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferInt;
+import java.io.FileReader;
+import java.io.File;
+import java.io.IOException;
+import java.util.Vector;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import javax.vecmath.Color3f;
+import javax.vecmath.Vector3f;
+import com.sun.j3d.utils.image.TextureLoader;
+import javax.media.j3d.Texture;
+import javax.media.j3d.Texture2D;
+import javax.media.j3d.ImageComponent;
+import javax.media.j3d.ImageComponent2D;
+import com.sun.j3d.loaders.lw3d.LWOBFileReader;
+import java.io.FileNotFoundException;
+import com.sun.j3d.loaders.ParsingErrorException;
+
+/**
+ * This class is responsible for parsing the binary data in an Object file
+ * that describes a texture for a particular surface and turning that data
+ * into Java3D texture data. If the texture is coming from a file (which
+ * is the only type of texture handled by the loader currently; other
+ * types of texture definitions are ignored), then this class instantiates
+ * a TargaReader object to read the data in that file. Once all of the
+ * data has been read, the class creates a Java3D Texture object by first
+ * scaling the image using the ImageScaler class (since all textures must
+ * have width/height = power of 2; Note: this functionality is now built
+ * into the TextureLoader class, so it could be removed from this loader)
+ * and then creating a Texture with that image.
+ */
+
+class LwoTexture extends ParserObject {
+
+ LWOBFileReader theReader;
+ int red = 255, green = 255, blue = 255;
+ Color3f color, diffuseColor, specularColor, emissiveColor;
+ Image theImage = null;
+ String imageFile = null;
+ Vector3f textureSize = new Vector3f(1f, 1f, 1f);;
+ Vector3f textureCenter = new Vector3f(0f, 0f, 0f);
+ int textureAxis;
+ int flags = 0;
+ String type;
+ String mappingType;
+ String nextToken = null;
+ static Hashtable imageTable = new Hashtable();
+ static Hashtable textureTable = new Hashtable();
+
+ /**
+ * Constructor: calls readTexture() to parse the file and retrieve
+ * texture parameters
+ */
+ LwoTexture(LWOBFileReader reader, int length, String typename,
+ int debugVals) throws FileNotFoundException {
+ super(debugVals);
+ debugOutputLn(TRACE, "Constructor");
+ theReader = reader;
+ type = typename;
+ readTexture(length);
+ }
+
+ String getNextToken() {
+ return nextToken;
+ }
+
+ /**
+ * The loader currently only handles CTEX and DTEX texture types
+ * (These either represent the surface color like a decal (CTEX)
+ * or modify the diffuse color (DTEX)
+ */
+ boolean isHandled() {
+ if ((type.equals("CTEX") ||
+ type.equals("DTEX")) &&
+ theImage != null)
+ return true;
+ debugOutputLn(LINE_TRACE, "failed isHandled(), type, theImage = " +
+ type + ", " + theImage);
+ return false;
+ }
+
+ /**
+ * Return the actual Texture object associated with the current image.
+ * If we've already created a texture for this image, return that;
+ * otherwise create a new Texture
+ */
+ Texture getTexture() {
+ debugOutputLn(TRACE, "getTexture()");
+ if (theImage == null)
+ return null;
+ Texture2D t2d = (Texture2D)textureTable.get(theImage);
+ if (t2d == null) {
+ ImageScaler scaler = new ImageScaler((BufferedImage)theImage);
+ BufferedImage scaledImage = (BufferedImage)scaler.getScaledImage();
+ TextureLoader tl = new TextureLoader(scaledImage);
+ t2d = (Texture2D)tl.getTexture();
+ textureTable.put(theImage, t2d);
+ }
+
+ return t2d;
+ }
+
+ String getType() {
+ return type;
+ }
+
+ Color3f getColor() {
+ return color;
+ }
+
+ Image getImage() {
+ return theImage;
+ }
+
+ Vector3f getTextureSize() {
+ return textureSize;
+ }
+
+ int getTextureAxis() {
+ return textureAxis;
+ }
+
+ Vector3f getTextureCenter() {
+ return textureCenter;
+ }
+
+ String getMappingType() {
+ return mappingType;
+ }
+
+ /**
+ * Parse the binary file to retrieve all texture parameters for this
+ * surface. If/when we encounter a TIMG parameter, which contains the
+ * filename of an image, then create a new TargaReader object to
+ * read that image file
+ */
+ void readTexture(int length)
+ throws FileNotFoundException, ParsingErrorException {
+
+ debugOutputLn(TRACE, "readTexture()");
+
+ int surfStopMarker = theReader.getMarker() + length;
+ mappingType = theReader.getString();
+ debugOutputLn(VALUES, "mappingType = " + mappingType);
+ String tokenString = theReader.getToken();
+ while (!(tokenString == null) && theReader.getMarker() < surfStopMarker) {
+
+ debugOutputLn(VALUES, " tokenString = " + tokenString);
+ debugOutputLn(VALUES, " marker, stop = " + theReader.getMarker() + ", " + surfStopMarker);
+
+ if (tokenString.endsWith("TEX") ||
+ (!tokenString.startsWith("T") || tokenString.equals("TRAN"))) {
+ nextToken = tokenString;
+ return;
+ }
+
+ int fieldLength = theReader.getShortInt();
+ debugOutputLn(VALUES, " fl = " + fieldLength);
+
+ if (tokenString.equals("TFLG")) {
+ debugOutputLn(WARNING, "Not yet handling: " + tokenString);
+ flags = theReader.getShortInt();
+ textureAxis = flags & 0x07;
+ debugOutputLn(WARNING, "val = " + flags);
+ }
+ else if (tokenString.equals("TCLR")) {
+ debugOutputLn(WARNING, "Not yet handling: " + tokenString);
+ try {
+ red = theReader.read();
+ green = theReader.read();
+ blue = theReader.read();
+ theReader.read();
+ }
+ catch (IOException e) {
+ throw new ParsingErrorException(e.getMessage());
+ }
+ debugOutputLn(WARNING, "val = " + red + ", " + green +
+ ", " + blue);
+ }
+ else if (tokenString.equals("TIMG")) {
+ debugOutputLn(WARNING, "Not yet handling: " + tokenString);
+ imageFile = theReader.getString();
+ debugOutputLn(VALUES, "imageFile = " + imageFile);
+ if (imageFile.indexOf("none") == -1) {
+ if ((theImage =
+ (Image)imageTable.get(imageFile)) == null) {
+ try {
+ TargaReader tr =
+ new TargaReader(imageFile,
+ debugPrinter.getValidOutput());
+ theImage = tr.getImage();
+ imageTable.put(imageFile, theImage);
+ }
+ catch (FileNotFoundException e) {
+ // Ignore texture if can't find it
+ debugOutputLn(WARNING, "Image File skipped: " +
+ imageFile);
+ }
+ }
+ }
+ debugOutputLn(WARNING, "val = __" + imageFile + "__");
+ }
+ else if (tokenString.equals("TWRP")) {
+ debugOutputLn(WARNING, "Not yet handling: " + tokenString);
+ int widthWrap = theReader.getShortInt();
+ int heightWrap = theReader.getShortInt();
+ debugOutputLn(WARNING, "val = " + widthWrap + ", " +
+ heightWrap);
+ }
+ else if (tokenString.equals("TCTR")) {
+ debugOutputLn(WARNING, "Not yet handling: " + tokenString);
+ textureCenter.x = theReader.getFloat();
+ textureCenter.y = theReader.getFloat();
+ textureCenter.z = theReader.getFloat();
+ debugOutputLn(WARNING, "val = " + textureCenter);
+ }
+ else if (tokenString.equals("TSIZ")) {
+ debugOutputLn(WARNING, "Not yet handling: " + tokenString);
+ textureSize.x = theReader.getFloat();
+ textureSize.y = theReader.getFloat();
+ textureSize.z = theReader.getFloat();
+ debugOutputLn(WARNING, "val = " + textureSize);
+ }
+ else {
+ debugOutputLn(WARNING,
+ "unrecognized token: " + tokenString);
+ theReader.skipLength(fieldLength);
+ }
+ if (theReader.getMarker() < surfStopMarker) {
+ tokenString = theReader.getToken();
+ }
+ }
+ }
+}
+
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/LwsBackground.java b/src/classes/share/com/sun/j3d/loaders/lw3d/LwsBackground.java
new file mode 100644
index 0000000..559857c
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/LwsBackground.java
@@ -0,0 +1,163 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+
+
+import java.io.*;
+import javax.media.j3d.*;
+import javax.vecmath.*;
+import java.util.Enumeration;
+import com.sun.j3d.loaders.ParsingErrorException;
+
+
+/**
+ * This class creates a Background object (solid color only, no geometry)
+ * according to some of the data stored in a Scene file. Note: Lightwave
+ * defines much more complex backgrounds that the loader currently
+ * handles. It should be possible to se Background Geometry to handle
+ * most of these cases, if there's time and will to work on the problem.
+ */
+
+class LwsBackground extends TextfileParser {
+
+ // data from the file
+ int solidBackdrop;
+ Color3f color, zenithColor, skyColor, groundColor, nadirColor;
+ Background backgroundObject = null;
+
+
+ /**
+ * Constructor: parses stream and retrieves all Background-related data
+ */
+ LwsBackground(StreamTokenizer st, int debugVals)
+ throws ParsingErrorException {
+
+ debugPrinter.setValidOutput(debugVals);
+ debugOutput(TRACE, "LwsBackground()");
+ color = new Color3f(0f, 0f, 0f);
+ zenithColor = new Color3f(0f, 0f, 0f);
+ skyColor = new Color3f(0f, 0f, 0f);
+ groundColor = new Color3f(0f, 0f, 0f);
+ nadirColor = new Color3f(0f, 0f, 0f);
+
+ solidBackdrop = (int)getNumber(st);
+ while (!isCurrentToken(st, "FogType")) {
+ debugOutputLn(LINE_TRACE, "currentToken = " + st.sval);
+
+ if (isCurrentToken(st, "BackdropColor")) {
+ color.x = (float)getNumber(st)/255f;
+ color.y = (float)getNumber(st)/255f;
+ color.z = (float)getNumber(st)/255f;
+ }
+ else if (isCurrentToken(st, "NadirColor")) {
+ nadirColor.x = (float)getNumber(st)/255f;
+ nadirColor.y = (float)getNumber(st)/255f;
+ nadirColor.z = (float)getNumber(st)/255f;
+ }
+ else if (isCurrentToken(st, "SkyColor")) {
+ skyColor.x = (float)getNumber(st)/255f;
+ skyColor.y = (float)getNumber(st)/255f;
+ skyColor.z = (float)getNumber(st)/255f;
+ }
+ else if (isCurrentToken(st, "GroundColor")) {
+ groundColor.x = (float)getNumber(st)/255f;
+ groundColor.y = (float)getNumber(st)/255f;
+ groundColor.z = (float)getNumber(st)/255f;
+ }
+ else if (isCurrentToken(st, "NadirColor")) {
+ nadirColor.x = (float)getNumber(st)/255f;
+ nadirColor.y = (float)getNumber(st)/255f;
+ nadirColor.z = (float)getNumber(st)/255f;
+ }
+ try {
+ st.nextToken();
+ }
+ catch (IOException e) {
+ throw new ParsingErrorException(e.getMessage());
+ }
+ }
+ st.pushBack(); // push token back on stack
+ }
+
+ /**
+ * Creates Java3d objects from the background data. Note that there
+ * are plenty of lw3d background attributes that the loader currently
+ * ignores. Some of these may best be handled by creating background
+ * geometry rather than a solid background color
+ */
+ void createJava3dObject() {
+ // TODO: there are various attributes of
+ // backdrops that we're not currently handling. In
+ // particular, if the file calls for a gradient background
+ // (solidBackdrop == 0), we ignore the request and just
+ // create a solid background from the sky color instead.
+ // We should be able to do something with the
+ // various colors specified, perhaps by creating
+ // background geometry with the appropriate vertex
+ // colors?
+
+ if (solidBackdrop != 0) {
+ backgroundObject = new Background(color);
+ debugOutput(VALUES, "Background color = " + color);
+ }
+ else {
+ backgroundObject = new Background(skyColor);
+ debugOutput(VALUES, "Background color = " + skyColor);
+ }
+ BoundingSphere bounds = new BoundingSphere(new Point3d(0.0,0.0,0.0), 100000.0);
+ backgroundObject.setApplicationBounds(bounds);
+ }
+
+ Background getObjectNode() {
+ return backgroundObject;
+ }
+
+ void printVals() {
+ debugOutputLn(VALUES, " BACKGROUND vals: ");
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/LwsCamera.java b/src/classes/share/com/sun/j3d/loaders/lw3d/LwsCamera.java
new file mode 100644
index 0000000..afc2a53
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/LwsCamera.java
@@ -0,0 +1,179 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+
+
+import java.io.*;
+import java.util.Vector;
+import javax.media.j3d.*;
+import javax.vecmath.*;
+import java.util.Enumeration;
+import com.sun.j3d.loaders.ParsingErrorException;
+
+/**
+ * This class parses the data in a Scene file related to the camera and
+ * creates Java3D TransformGroup that holds the data for positioning
+ * and orienting the view according to the camera specifications.
+ */
+
+class LwsCamera extends TextfileParser implements LwsPrimitive {
+
+ // data from the file
+ String fileName;
+ String objName;
+ LwsMotion motion;
+ int parent;
+ TransformGroup objectTransform;
+ Vector objectBehavior;
+
+ /**
+ * Constructor: parses camera info and creates LwsMotion object for
+ * keyframe data
+ */
+ LwsCamera(StreamTokenizer st, int firstFrame,
+ int totalFrames, float totalTime,
+ int debugVals) throws ParsingErrorException {
+ debugPrinter.setValidOutput(debugVals);
+ parent = -1;
+ getNumber(st); // Skip ShowCamera parameters
+ getNumber(st);
+ getAndCheckString(st, "CameraMotion");
+ motion = new LwsMotion(st, firstFrame, totalFrames, totalTime,
+ debugPrinter.getValidOutput());
+
+ // TODO: buggy way to stop processing the camera. Should actually
+ // process required/optional fields in order and stop when there's
+ // no more.
+
+ while (!isCurrentToken(st, "DepthOfField")) {
+ debugOutputLn(LINE_TRACE, "currentToken = " + st.sval);
+
+ if (isCurrentToken(st, "ParentObject")) {
+ parent = (int)getNumber(st);
+ }
+ try {
+ st.nextToken();
+ }
+ catch (IOException e) {
+ throw new ParsingErrorException(e.getMessage());
+ }
+ }
+ getNumber(st); // skip shadow type parameter
+ }
+
+ /**
+ * Returns parent of the camera object
+ */
+ int getParent() {
+ return parent;
+ }
+
+ /**
+ * Creates Java3D items from the camera data. These objects consist
+ * of: a TransformGroup to hold the view platform, and the behaviors
+ * (if any) that act upon the view's TransformGroup.
+ */
+ void createJava3dObject(int loadBehaviors)
+ {
+ Matrix4d mat = new Matrix4d();
+ mat.setIdentity();
+ // Set the node's transform matrix according to the first frame
+ // of the object's motion
+ LwsFrame firstFrame = motion.getFirstFrame();
+ firstFrame.setMatrix(mat);
+ debugOutputLn(VALUES, " Camera Matrix = \n" + mat);
+ Transform3D t1 = new Transform3D();
+ Matrix4d m = new Matrix4d();
+ double scale = .1;
+ m.setColumn(0, scale, 0, 0, 0); // setScale not yet implemented
+ m.setColumn(1, 0, scale, 0, 0);
+ m.setColumn(2, 0, 0, scale, 0);
+ m.setColumn(3, 0, 0, 0, 1);
+ Transform3D scaleTrans = new Transform3D(m);
+ TransformGroup scaleGroup = new TransformGroup(scaleTrans);
+ scaleGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
+ scaleGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_READ);
+ // mat.mul(m);
+ t1.set(mat);
+ objectTransform = new TransformGroup(t1);
+ objectTransform.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
+ objectBehavior = new Vector();;
+ if (loadBehaviors != 0) {
+ motion.createJava3dBehaviors(objectTransform);
+ Behavior b = motion.getBehaviors();
+ if (b != null)
+ objectBehavior.addElement(b);
+ }
+ }
+
+ /**
+ * Returns TransformGroup of camera
+ */
+ public TransformGroup getObjectNode()
+ {
+ return objectTransform;
+ }
+
+ /**
+ * Returns animation behaviors for camera
+ */
+ public Vector getObjectBehaviors()
+ {
+ debugOutputLn(TRACE, "getObjectBehaviors()");
+ return objectBehavior;
+ }
+
+ /**
+ * This is a debuggin utility, not currently activated. It prints
+ * out the camera values
+ */
+ void printVals()
+ {
+ System.out.println(" objName = " + objName);
+ motion.printVals();
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/LwsEnvelope.java b/src/classes/share/com/sun/j3d/loaders/lw3d/LwsEnvelope.java
new file mode 100644
index 0000000..e2b994e
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/LwsEnvelope.java
@@ -0,0 +1,160 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+import java.io.*;
+import javax.media.j3d.*;
+import javax.vecmath.*;
+import com.sun.j3d.internal.J3dUtilsI18N;
+import com.sun.j3d.loaders.ParsingErrorException;
+import com.sun.j3d.loaders.IncorrectFormatException;
+
+/**
+ * This class is a superclass for any implementation of envelopes; the
+ * only subclass currently is LwsEnvelopeLightIntensity. LwsEnvelope
+ * parses the data in a Scene file and extracts the envelope data.
+ */
+
+class LwsEnvelope extends TextfileParser {
+
+ // data from the file
+ String name;
+ LwsEnvelopeFrame frames[];
+ int numFrames;
+ int numChannels;
+ boolean loop;
+ float totalTime;
+ int totalFrames;
+ Behavior behaviors;
+
+ /**
+ * Constructor: calls getEnvelope() to parse the stream for the
+ * envelope data
+ */
+ LwsEnvelope(StreamTokenizer st, int frames, float time) {
+ numFrames = 0;
+ totalTime = time;
+ totalFrames = frames;
+ name = getName(st);
+ getEnvelope(st);
+ }
+
+ /**
+ * Parses the stream to retrieve all envelope data. Creates
+ * LwsEnvelopeFrame objects for each keyframe of the envelope
+ * (these frames differ slightly from LwsFrame objects because
+ * envelopes contain slightly different data)
+ */
+ void getEnvelope(StreamTokenizer st)
+ throws IncorrectFormatException, ParsingErrorException
+ {
+ debugOutputLn(TRACE, "getEnvelope()");
+ numChannels = (int)getNumber(st);
+ if (numChannels != 1) {
+ throw new IncorrectFormatException(
+ J3dUtilsI18N.getString("LwsEnvelope0"));
+ }
+ debugOutputLn(LINE_TRACE, "got channels");
+
+ numFrames = (int)getNumber(st);
+ frames = new LwsEnvelopeFrame[numFrames];
+ debugOutputLn(VALUES, "got frames" + numFrames);
+
+ for (int i = 0; i < numFrames; ++i) {
+ frames[i] = new LwsEnvelopeFrame(st);
+ }
+ debugOutput(LINE_TRACE, "got all frames");
+
+ try {
+ st.nextToken();
+ while (!isCurrentToken(st, "EndBehavior")) {
+ // There is an undocumented "FrameOffset" in some files
+ st.nextToken();
+ }
+ }
+ catch (IOException e) {
+ throw new ParsingErrorException(e.getMessage());
+ }
+ int repeatVal = (int)getNumber(st);
+ if (repeatVal == 1)
+ loop = false;
+ else
+ loop = true;
+ }
+
+ /**
+ * This superclass does nothing here - if the loader understands
+ * how to deal with a particular type of envelope, it will use
+ * a subclass of LwsEnvelope that will override this method
+ */
+ void createJava3dBehaviors(TransformGroup target) {
+ behaviors = null;
+ }
+
+ Behavior getBehaviors() {
+ return behaviors;
+ }
+
+
+ LwsEnvelopeFrame getFirstFrame() {
+ if (numFrames > 0)
+ return frames[0];
+ else
+ return null;
+ }
+
+
+ void printVals() {
+ debugOutputLn(VALUES, " name = " + name);
+ debugOutputLn(VALUES, " numChannels = " + numChannels);
+ debugOutputLn(VALUES, " numFrames = " + numFrames);
+ debugOutputLn(VALUES, " loop = " + loop);
+ for (int i = 0; i < numFrames; ++i) {
+ debugOutputLn(VALUES, " FRAME " + i);
+ frames[i].printVals();
+ }
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/LwsEnvelopeFrame.java b/src/classes/share/com/sun/j3d/loaders/lw3d/LwsEnvelopeFrame.java
new file mode 100644
index 0000000..587175f
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/LwsEnvelopeFrame.java
@@ -0,0 +1,105 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+
+
+import java.io.*;
+import javax.vecmath.Matrix4d;
+import javax.vecmath.Vector3d;
+import javax.vecmath.Point3f;
+
+/**
+ * This class represents one keyframe in an envelope sequence.
+ */
+
+class LwsEnvelopeFrame extends TextfileParser {
+
+ // data from the file
+ double value;
+ double frameNumber;
+ int linearValue;
+ double tension, continuity, bias;
+
+
+ /**
+ * Constructor: parses stream and stores data for one keyframe of
+ * an envelope sequence
+ */
+ LwsEnvelopeFrame(StreamTokenizer st) {
+ value = getNumber(st);
+ debugOutputLn(VALUES, "value = " + value);
+ frameNumber = (int)getNumber(st);
+ linearValue = (int)getNumber(st);
+ debugOutputLn(VALUES, "framenum, linear " + frameNumber + " , " + linearValue);
+ tension = getNumber(st);
+ continuity = getNumber(st);
+ bias = getNumber(st);
+ debugOutputLn(VALUES, "tension, cont, bias = " + tension + ", " + continuity + ", " + bias);
+ //System.out.println(" FRAME VALS");
+ //printVals();
+ }
+
+
+ double getValue() {
+ return value;
+ }
+
+
+ double getFrameNum() {
+ return frameNumber;
+ }
+
+
+ void printVals() {
+ debugOutputLn(VALUES, " value = " + value);
+ debugOutputLn(VALUES, " frameNum = " + frameNumber);
+ debugOutputLn(VALUES, " lin = " + linearValue);
+ debugOutputLn(VALUES, " tension = " + tension);
+ debugOutputLn(VALUES, " continuity = " + continuity);
+ debugOutputLn(VALUES, " bias = " + bias);
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/LwsEnvelopeLightIntensity.java b/src/classes/share/com/sun/j3d/loaders/lw3d/LwsEnvelopeLightIntensity.java
new file mode 100644
index 0000000..e241b5c
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/LwsEnvelopeLightIntensity.java
@@ -0,0 +1,153 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+
+
+import java.io.*;
+import javax.media.j3d.*;
+import javax.vecmath.*;
+import javax.media.j3d.TransformGroup;
+
+
+/**
+ * This class creates a LightIntensityPathInterpolator object from the
+ * keyframe-based envelope specified in a Scene file.
+ */
+
+class LwsEnvelopeLightIntensity extends LwsEnvelope {
+
+
+ /**
+ * Constructor: Calls superclass, which will parse the stream
+ * and store the envelope data
+ */
+ LwsEnvelopeLightIntensity(StreamTokenizer st,
+ int frames, float time) {
+ super(st, frames, time);
+ }
+
+ /**
+ * Creates Java3d behaviors given the stored envelope data. The
+ * Behavior created is a LightIntensityPathInterpolator
+ */
+ void createJava3dBehaviors(Object target) {
+ if (numFrames <= 1)
+ behaviors = null;
+ else {
+ long alphaAtOne = 0;
+ int loopCount;
+ if (loop)
+ loopCount = -1;
+ else
+ loopCount = 1;
+ // Note: hardcoded to always loop...
+ loopCount = -1;
+ debugOutputLn(VALUES, "totalTime = " + totalTime);
+ debugOutputLn(VALUES, "loopCount = " + loopCount);
+ float animTime = 1000.0f * totalTime *
+ (float)(frames[numFrames-1].getFrameNum()/(float)totalFrames);
+ debugOutputLn(VALUES, " anim time: " + animTime);
+ debugOutputLn(VALUES, " totalFrames = " + totalFrames);
+ debugOutputLn(VALUES, " lastFrame = " +
+ frames[numFrames-1].getFrameNum());
+ if (!loop)
+ alphaAtOne = (long)(1000.0*totalTime - animTime);
+ Alpha theAlpha =
+ new Alpha(loopCount, Alpha.INCREASING_ENABLE,
+ 0, 0, (long)animTime, 0,
+ alphaAtOne, 0, 0, 0);
+ float knots[] = new float[numFrames];
+ float values[] = new float[numFrames];
+ for (int i=0; i < numFrames; ++i) {
+ values[i] = (float)frames[i].getValue();
+ knots[i] = (float)(frames[i].getFrameNum())/
+ (float)(frames[numFrames-1].getFrameNum());
+ debugOutputLn(VALUES, "value, knot = " +
+ values[i] + ", " + knots[i]);
+ }
+ LightIntensityPathInterpolator l = new
+ LightIntensityPathInterpolator(theAlpha,
+ knots,
+ values,
+ target);
+ if (l != null) {
+ behaviors = l;
+ BoundingSphere bounds =
+ new BoundingSphere(new Point3d(0.0,0.0,0.0), 1000000.0);
+ behaviors.setSchedulingBounds(bounds);
+ ((TransformGroup)target).setCapability
+ (TransformGroup.ALLOW_TRANSFORM_WRITE);
+ ((TransformGroup)target).addChild(behaviors);
+ }
+ }
+ }
+
+
+ Behavior getBehaviors() {
+ return behaviors;
+ }
+
+
+ LwsEnvelopeFrame getFirstFrame() {
+ if (numFrames > 0)
+ return frames[0];
+ else
+ return null;
+ }
+
+
+ void printVals() {
+ debugOutputLn(VALUES, " name = " + name);
+ debugOutputLn(VALUES, " numChannels = " + numChannels);
+ debugOutputLn(VALUES, " numFrames = " + numFrames);
+ debugOutputLn(VALUES, " loop = " + loop);
+ for (int i = 0; i < numFrames; ++i) {
+ debugOutputLn(VALUES, " FRAME " + i);
+ frames[i].printVals();
+ }
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/LwsFog.java b/src/classes/share/com/sun/j3d/loaders/lw3d/LwsFog.java
new file mode 100644
index 0000000..2f8f83f
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/LwsFog.java
@@ -0,0 +1,143 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+
+
+import java.io.*;
+import javax.media.j3d.*;
+import javax.vecmath.*;
+import java.util.Enumeration;
+import com.sun.j3d.loaders.ParsingErrorException;
+
+
+/**
+ * This class creates a Fog object from the data in a Scene file.
+ */
+
+class LwsFog extends TextfileParser {
+
+ // data from the file
+ float minDist, maxDist, minAmount, maxAmount;
+ int backdropFog;
+ Color3f color;
+ int type;
+ Fog fogObject = null;
+
+ /**
+ * Constructor: parses stream and stores fog data
+ */
+ LwsFog(StreamTokenizer st, int debugVals) throws ParsingErrorException {
+ debugPrinter.setValidOutput(debugVals);
+ debugOutput(TRACE, "LwsFog()");
+ color = new Color3f(0f, 0f, 0f);
+
+ while (!isCurrentToken(st, "DitherIntensity")) {
+ debugOutputLn(LINE_TRACE, "currentToken = " + st.sval);
+
+ if (isCurrentToken(st, "FogMinDist")) {
+ minDist = (float)getNumber(st);
+ }
+ else if (isCurrentToken(st, "FogMaxDist")) {
+ maxDist = (float)getNumber(st);
+ }
+ else if (isCurrentToken(st, "FogMinAmount")) {
+ minAmount = (float)getNumber(st);
+ }
+ else if (isCurrentToken(st, "FogMaxAmount")) {
+ maxAmount = (float)getNumber(st);
+ }
+ else if (isCurrentToken(st, "BackdropFog")) {
+ backdropFog = (int)getNumber(st);
+ }
+ else if (isCurrentToken(st, "FogColor")) {
+ color.x = (float)getNumber(st)/255f;
+ color.y = (float)getNumber(st)/255f;
+ color.z = (float)getNumber(st)/255f;
+ }
+ try {
+ st.nextToken();
+ }
+ catch (IOException e) {
+ throw new ParsingErrorException(e.getMessage());
+ }
+ }
+ st.pushBack(); // push token back on stack
+ }
+
+ /**
+ * Creates Java3d Fog object given the fog parameters in the file.
+ * Note that various fog parameters in lw3d are not currently handled.
+ */
+ void createJava3dObject() {
+ // TODO: there are various attributes of lw fog that
+ // we're not currently handing, including non-linear fog
+ // (need to understand the two different types - these may
+ // map onto java3d's expontential fog node), non-solid
+ // backdrop colors (how to handle this?), min/max amount
+ // (j3d only handles 0 -> 1 case)
+
+ fogObject = new LinearFog(color, minDist, maxDist);
+ debugOutputLn(VALUES,
+ "just set linearFog with color, minDist, maxDist = " +
+ color + ", " +
+ minDist + ", " +
+ maxDist);
+ BoundingSphere bounds = new BoundingSphere(new Point3d(0.0,0.0,0.0), 100000.0);
+ fogObject.setInfluencingBounds(bounds);
+ }
+
+ Fog getObjectNode()
+ {
+ return fogObject;
+ }
+
+ void printVals()
+ {
+ debugOutputLn(VALUES, " FOG vals: ");
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/LwsFrame.java b/src/classes/share/com/sun/j3d/loaders/lw3d/LwsFrame.java
new file mode 100644
index 0000000..f779eb4
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/LwsFrame.java
@@ -0,0 +1,341 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+
+
+import java.io.*;
+import javax.vecmath.Matrix4d;
+import javax.vecmath.Vector3d;
+import javax.vecmath.Point3f;
+
+/**
+ * This class is responsible for parsing the data in a Scene file
+ * associated with a single keyframe. This data includes the position,
+ * orientation, and scaling information, in addition to the frame number
+ * of that keyframe and some spline controls which are currently
+ * ignored.
+ */
+
+class LwsFrame extends TextfileParser {
+
+ // data from the file
+ double x, y, z;
+ double heading, pitch, bank;
+ double xScale, yScale, zScale;
+ double frameNumber;
+ int linearValue;
+ double tension, continuity, bias;
+
+ /**
+ * Constructor: parses and stores all data associated with a particular
+ * keyframe
+ */
+ LwsFrame(StreamTokenizer st) {
+ x = getNumber(st);
+ y = getNumber(st);
+ z = -getNumber(st);
+ debugOutputLn(VALUES, "x, y, z " + x + ", " + y + ", " + z);
+ heading = getNumber(st);
+ pitch = getNumber(st);
+ bank = getNumber(st);
+ debugOutputLn(VALUES, "(degrees) h, p, b = " + heading + ", " + pitch + ", " + bank);
+ heading *= (Math.PI / 180.0); // Java3d works with radians
+ pitch *= (Math.PI / 180.0);
+ bank *= (Math.PI / 180.0);
+ debugOutputLn(VALUES, "(radians) h, p, b = " + heading + ", " + pitch + ", " + bank);
+ debugOutputLn(LINE_TRACE, "got pos and ori");
+ xScale = getNumber(st);
+ yScale = getNumber(st);
+ zScale = getNumber(st);
+ debugOutputLn(VALUES, "xs, ys, zs " + xScale +", " + yScale + ", " + zScale);
+ frameNumber = (int)getNumber(st);
+ // Note: The following spline controls are ignored
+ linearValue = (int)getNumber(st);
+ debugOutputLn(VALUES, "framenum, linear " + frameNumber + " , " + linearValue);
+ tension = getNumber(st);
+ continuity = getNumber(st);
+ bias = getNumber(st);
+ debugOutputLn(VALUES, "tension, cont, bias = " + tension + ", " + continuity + ", " + bias);
+ }
+
+
+
+ /**
+ * Construct new frame that's in-between two given frames
+ * Ratio gives the interpolation value for how far in-between
+ * the new frame should be (0.5 is half-way, etc)
+ */
+ LwsFrame(LwsFrame prevFrame, LwsFrame nextFrame, double ratio) {
+
+ x = prevFrame.x + (nextFrame.x - prevFrame.x) * ratio;
+ y = prevFrame.y + (nextFrame.y - prevFrame.y) * ratio;
+ z = prevFrame.z + (nextFrame.z - prevFrame.z) * ratio;
+
+ heading = prevFrame.heading +
+ (nextFrame.heading - prevFrame.heading) * ratio;
+ pitch = prevFrame.pitch +
+ (nextFrame.pitch - prevFrame.pitch) * ratio;
+ bank = prevFrame.bank +
+ (nextFrame.bank - prevFrame.bank) * ratio;
+ xScale = prevFrame.xScale +
+ (nextFrame.xScale - prevFrame.xScale) * ratio;
+ yScale = prevFrame.yScale +
+ (nextFrame.yScale - prevFrame.yScale) * ratio;
+ zScale = prevFrame.zScale +
+ (nextFrame.zScale - prevFrame.zScale) * ratio;
+ frameNumber = prevFrame.frameNumber +
+ (nextFrame.frameNumber - prevFrame.frameNumber) * ratio;
+
+ // The following are not interpolated
+ linearValue = prevFrame.linearValue;
+ tension = prevFrame.tension;
+ continuity = prevFrame.continuity;
+ bias = prevFrame.bias;
+ }
+
+ /**
+ * Using hermite interpolation construct a new frame that's
+ * in-between two given frames. We also need to be given a
+ * frame before the first frame and a frame after the second
+ * frame. The calling function will make sure that we get the
+ * four appropriate frames.
+ *
+ * Ratio gives the interpolation value for how far in-between
+ * the new frame should be. (.5 is half-way, etc.)
+ */
+ LwsFrame(LwsFrame prevFrame, LwsFrame frame1,
+ LwsFrame frame2, LwsFrame nextFrame, double u,
+ double adj0, double adj1) {
+
+ double h1, h2, h3, h4;
+ double dd0a, dd0b, ds1a, ds1b;
+
+ // pre-compute spline coefficients
+ double u2, u3, z1;
+ u2 = u * u;
+ u3 = u2 *u;
+ z1 = 3.0f *u2 - u3 - u3;
+ h1 = 1.0f - z1;
+ h2 = z1;
+ h3 = u3 - u2 - u2 + u;
+ h4 = u3 - u2;
+
+ dd0a = (1.0f - frame1.tension) * (1.0f + frame1.continuity)
+ * (1.0f + frame1.bias);
+
+ dd0b = (1.0f - frame1.tension) * (1.0f - frame1.continuity)
+ * (1.0f - frame1.bias);
+
+ ds1a = (1.0f - frame2.tension) * (1.0f - frame2.continuity)
+ * (1.0f + frame2.bias);
+
+ ds1b = (1.0f - frame2.tension) * (1.0f + frame2.continuity)
+ * (1.0f - frame2.bias);
+
+ double[] v = new double[4];
+
+ // interpolate x, y, z
+ v[0] = prevFrame.x; v[1] = frame1.x;
+ v[2] = frame2.x; v[3] = nextFrame.x;
+ x = computeInterpolation (v, dd0a, dd0b, ds1a, ds1b,
+ adj0, adj1, h1, h2, h3, h4);
+ v[0] = prevFrame.y; v[1] = frame1.y;
+ v[2] = frame2.y; v[3] = nextFrame.y;
+ y = computeInterpolation (v, dd0a, dd0b, ds1a, ds1b,
+ adj0, adj1, h1, h2, h3, h4);
+ v[0] = prevFrame.z; v[1] = frame1.z;
+ v[2] = frame2.z; v[3] = nextFrame.z;
+ z = computeInterpolation (v, dd0a, dd0b, ds1a, ds1b,
+ adj0, adj1, h1, h2, h3, h4);
+
+ // interpolate heading pitch and bank
+ v[0] = prevFrame.heading; v[1] = frame1.heading;
+ v[2] = frame2.heading ; v[3] = nextFrame.heading;
+ heading = computeInterpolation (v, dd0a, dd0b, ds1a, ds1b,
+ adj0, adj1, h1, h2, h3, h4);
+
+ v[0] = prevFrame.pitch; v[1] = frame1.pitch;
+ v[2] = frame2.pitch; v[3] = nextFrame.pitch;
+ pitch = computeInterpolation (v, dd0a, dd0b, ds1a, ds1b,
+ adj0, adj1, h1, h2, h3, h4);
+
+ v[0] = prevFrame.bank; v[1] = frame1.bank;
+ v[2] = frame2.bank; v[3] = nextFrame.bank;
+ bank = computeInterpolation (v, dd0a, dd0b, ds1a, ds1b,
+ adj0, adj1, h1, h2, h3, h4);
+
+ // interpolate scale - scale interpolation is assumed to be linear
+ xScale = frame1.xScale + (frame2.xScale - frame1.xScale) * u;
+ yScale = frame1.yScale + (frame2.yScale - frame1.yScale) * u;
+ zScale = frame1.zScale + (frame2.zScale - frame1.zScale) * u;
+
+ // interpolate frame number
+ frameNumber = frame1.frameNumber +
+ (frame2.frameNumber - frame1.frameNumber) * u;
+
+ // The following are not interpolated
+ linearValue = frame2.linearValue;
+
+ // We need to keep the spline smooth between knot points
+ tension = 0.0;
+ continuity = 0.0;
+ bias = 0.0;
+ }
+
+
+ double computeInterpolation(double[] value, double dd0a,
+ double dd0b, double ds1a,
+ double ds1b, double adj0,
+ double adj1, double h1,
+ double h2, double h3, double h4) {
+
+ double dd0, ds1;
+ double delta = value[2] - value[1] ;
+ double result;
+
+ // if adj != 0
+ if (adj0 < -0.0001 || adj0 > 0.0001)
+ dd0 = adj0 * (dd0a * (value[1] - value[0]) + dd0b * delta);
+ else
+ dd0 = 0.5f * (dd0a + dd0b) * delta;
+
+ // if adj != 0
+ if (adj1 < -0.0001 || adj1 > 0.0001)
+ ds1 = adj1 * (ds1a * delta + ds1b * (value[3] - value[2]));
+ else
+ ds1 = 0.5f * (ds1a + ds1b) * delta;
+
+ result = value[1] * h1 + value[2] * h2 + dd0 * h3 + ds1 * h4;
+
+ return (result);
+ }
+
+ double getHeading() {
+ return heading;
+ }
+
+ double getPitch() {
+ return pitch;
+ }
+
+ double getBank() {
+ return bank;
+ }
+
+ /**
+ * Sets the given matrix to contain the position, orientation, and
+ * scale values for the keyframe
+ */
+ void setMatrix(Matrix4d mat) {
+ setRotationMatrix(mat);
+ mat.setTranslation(new Vector3d(x, y, z));
+ Matrix4d m = new Matrix4d();
+ m.setColumn(0, xScale, 0, 0, 0); // setScale not yet implemented
+ m.setColumn(1, 0, yScale, 0, 0);
+ m.setColumn(2, 0, 0, zScale, 0);
+ m.setColumn(3, 0, 0, 0, 1);
+ mat.mul(m);
+ }
+
+ /**
+ * Sets the given matrix to contain the orientation for this keyframe
+ */
+ void setRotationMatrix(Matrix4d mat)
+ {
+ debugOutputLn(TRACE, "setRotMat()");
+ debugOutputLn(VALUES, " p, h, b = " +
+ pitch + ", " +
+ heading + ", " +
+ bank);
+ Matrix4d pitchMat = new Matrix4d();
+ pitchMat.rotX(-pitch);
+ Matrix4d bankMat = new Matrix4d();
+ bankMat.rotZ(bank);
+ mat.rotY(-heading);
+ mat.mul(pitchMat);
+ mat.mul(bankMat);
+ debugOutputLn(VALUES, "setRotMat(), mat = " + mat);
+ }
+
+ Point3f getPosition() {
+ return (new Point3f((float)x, (float)y, (float)z));
+ }
+
+ Point3f getScale() {
+ // Make sure we don't have zero scale components
+ if ((xScale < -0.0001 || xScale > 0.0001) &&
+ (yScale < -0.0001 || yScale > 0.0001) &&
+ (zScale < -0.0001 || zScale > 0.0001)) {
+ return (new Point3f((float)xScale, (float)yScale, (float)zScale));
+ } else {
+ return (new Point3f(1.0f, 1.0f, 1.0f));
+ }
+ }
+
+ double getFrameNum() {
+ return frameNumber;
+ }
+
+ void printVals() {
+ debugOutputLn(VALUES, " x = " + x);
+ debugOutputLn(VALUES, " y = " + y);
+ debugOutputLn(VALUES, " z = " + z);
+ debugOutputLn(VALUES, " xScale = " + xScale);
+ debugOutputLn(VALUES, " yScale = " + yScale);
+ debugOutputLn(VALUES, " zScale = " + zScale);
+ debugOutputLn(VALUES, " heading = " + heading);
+ debugOutputLn(VALUES, " pitch = " + pitch);
+ debugOutputLn(VALUES, " bank = " + bank);
+ debugOutputLn(VALUES, " frameNum = " + frameNumber);
+ debugOutputLn(VALUES, " lin = " + linearValue);
+ debugOutputLn(VALUES, " tension = " + tension);
+ debugOutputLn(VALUES, " continuity = " + continuity);
+ debugOutputLn(VALUES, " bias = " + bias);
+ }
+
+}
+
+
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/LwsLight.java b/src/classes/share/com/sun/j3d/loaders/lw3d/LwsLight.java
new file mode 100644
index 0000000..89bd19b
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/LwsLight.java
@@ -0,0 +1,269 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+
+
+import java.io.*;
+import java.util.Vector;
+import javax.media.j3d.*;
+import javax.vecmath.*;
+import java.util.Enumeration;
+import com.sun.j3d.loaders.ParsingErrorException;
+
+/**
+ * This class creates a light object from the data in a Scene file. It
+ * instantiates an LwsMotion object to create any associated
+ * animations.
+ */
+
+class LwsLight extends TextfileParser implements LwsPrimitive {
+
+ // data from the file
+ String fileName;
+ String objName;
+ LwsMotion motion;
+ int parent;
+ TransformGroup objectTransform;
+ Vector objectBehavior;
+ Color3f color;
+ int type;
+ Point3f attenuation = new Point3f(1.0f, 0.0f, 0.0f);
+ float spotConeAngle = (float)(Math.PI);
+ // Meta object, used for holding light and
+ LwLightObject lwLight;
+ // light parameters
+ LwsEnvelopeLightIntensity intensityEnvelope = null;
+ Light light = null;
+ final static int DIRECTIONAL = 0, POINT = 1, SPOT = 2;
+
+ /**
+ * Constructor: parses stream and creates data structures for all
+ * light parameters currently handled by the loader
+ */
+ LwsLight(StreamTokenizer st, int totalFrames, float totalTime,
+ int debugVals) throws ParsingErrorException {
+
+ debugPrinter.setValidOutput(debugVals);
+
+ debugOutput(TRACE, "LwsLight()");
+ color = new Color3f(1f, 1f, 1f);
+ lwLight = new LwLightObject(null, 0.0f, null);
+
+ parent = -1;
+ debugOutputLn(LINE_TRACE, "about to get LightName");
+ getAndCheckString(st, "LightName");
+ debugOutputLn(LINE_TRACE, "about to get LightName value");
+ objName = getName(st);
+ debugOutputLn(LINE_TRACE, "got LightName");
+ skip(st, "ShowLight", 2);
+ debugOutputLn(LINE_TRACE, "got ShowLight");
+ getAndCheckString(st, "LightMotion");
+ debugOutputLn(LINE_TRACE, "got LightMotion");
+ motion = new LwsMotion(st, totalFrames, totalTime);
+ debugOutputLn(LINE_TRACE, "got motions");
+
+ // TODO: buggy way to stop processing the light. Should actually
+ // process required/optional fields in order and stop when there's
+ // no more. However, spec says "ShadowCasing" but the files say
+ // "ShadowType".
+
+ while (!isCurrentToken(st, "ShowCamera") &&
+ !isCurrentToken(st, "AddLight")) {
+ // TODO:
+ // Things that we're not yet processing (and should):
+ // - EdgeAngle: for spotlights, this is the angle which
+ // contains the linear falloff toward the edge of the
+ // ConeAngle. This doesn't directly map to J3d's
+ // "concentration" value, so it's left out for now.
+
+ debugOutputLn(LINE_TRACE, "currentToken = " + st.sval);
+
+ if (isCurrentToken(st, "ParentObject")) {
+ parent = (int)getNumber(st);
+ }
+ else if (isCurrentToken(st, "LightColor")) {
+ color.x = (float)getNumber(st)/255f;
+ color.y = (float)getNumber(st)/255f;
+ color.z = (float)getNumber(st)/255f;
+ lwLight.setColor(color);
+ }
+ else if (isCurrentToken(st, "LgtIntensity")) {
+ // TODO: must be able to handle envelopes here
+ String className = getClass().getName();
+ int classIndex = className.lastIndexOf('.');
+ String packageName;
+ if (classIndex < 0)
+ packageName = "";
+ else
+ packageName = className.substring(0, classIndex) + ".";
+ EnvelopeHandler env =
+ new EnvelopeHandler(st, totalFrames, totalTime,
+ packageName + "LwsEnvelopeLightIntensity");
+ if (env.hasValue) {
+ float intensity = (float)env.theValue;
+ color.x *= intensity;
+ color.y *= intensity;
+ color.z *= intensity;
+ lwLight.setIntensity(intensity);
+ }
+ else {
+ intensityEnvelope =
+ (LwsEnvelopeLightIntensity)env.theEnvelope;
+ }
+ }
+ else if (isCurrentToken(st, "LightType")) {
+ type = (int)getNumber(st);
+ }
+ else if (isCurrentToken(st, "Falloff")) {
+ float falloff = (float)getNumber(st);
+ attenuation.y = 1.0f/(1.0f - falloff) - 1.0f;
+ }
+ else if (isCurrentToken(st, "ConeAngle")) {
+ spotConeAngle = (float)getNumber(st) * (float)(Math.PI / 180.0);
+ }
+ try {
+ st.nextToken();
+ }
+ catch (IOException e) {
+ throw new ParsingErrorException(e.getMessage());
+ }
+ }
+ st.pushBack(); // push "ShowCamera" or "AddLight" back on stack
+ }
+
+ int getParent() {
+ return parent;
+ }
+
+ /**
+ * Create Java3D objects from the data we got from the file
+ */
+ void createJava3dObject(int loadBehaviors) {
+ Matrix4d mat = new Matrix4d();
+ mat.setIdentity();
+ // Set the node's transform matrix according to the first frame
+ // of the object's motion
+ LwsFrame firstFrame = motion.getFirstFrame();
+ firstFrame.setMatrix(mat);
+ debugOutputLn(VALUES, "Light transform = " + mat);
+ Transform3D t1 = new Transform3D();
+ t1.set(mat);
+ objectTransform = new TransformGroup(t1);
+ objectTransform.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
+ Vector3f defaultDir = new Vector3f(0f, 0f, -1f);
+ Point3f defaultPos = new Point3f(0f, 0f, 0f);
+
+ switch (type) {
+ case DIRECTIONAL:
+ light = new DirectionalLight(color, defaultDir);
+ break;
+ case POINT:
+ light = new PointLight(color, defaultPos, attenuation);
+ break;
+ case SPOT:
+ // Note: spotConeAngle in lw3d is half that of Java3d...
+ light = new SpotLight(color, defaultPos, attenuation, defaultDir,
+ 2 * spotConeAngle, 0.0f);
+ break;
+ default:
+ // Shouldn't get here
+ break;
+ }
+
+ light.setCapability(Light.ALLOW_COLOR_WRITE);
+ if (light != null) {
+ lwLight.setLight(light);
+ BoundingSphere bounds =
+ new BoundingSphere(new Point3d(0.0,0.0,0.0), 100000.0);
+ light.setInfluencingBounds(bounds);
+ objectTransform.addChild(light);
+
+ // load behaviors if we have to
+ objectBehavior = new Vector();
+ if (loadBehaviors != 0) {
+ Behavior b;
+ b = null;
+ motion.createJava3dBehaviors(objectTransform);
+ b = motion.getBehaviors();
+ if (b != null)
+ objectBehavior.addElement(b);
+
+ if (intensityEnvelope != null) {
+ b = null;
+ intensityEnvelope.createJava3dBehaviors(lwLight);
+ b = intensityEnvelope.getBehaviors();
+ if (b != null)
+ objectBehavior.addElement(b);
+ }
+ }
+ }
+ }
+
+ public TransformGroup getObjectNode()
+ {
+ return objectTransform;
+ }
+
+ Light getLight() {
+ return light;
+ }
+
+ public Vector getObjectBehaviors()
+ {
+ debugOutputLn(TRACE, "getObjectBehaviors()");
+ return objectBehavior;
+ }
+
+
+ void printVals()
+ {
+ debugOutputLn(VALUES, " LIGHT vals: ");
+ debugOutputLn(VALUES, " objName = " + objName);
+ motion.printVals();
+ }
+
+
+}
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/LwsMotion.java b/src/classes/share/com/sun/j3d/loaders/lw3d/LwsMotion.java
new file mode 100644
index 0000000..472a2f9
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/LwsMotion.java
@@ -0,0 +1,688 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+import java.io.*;
+import java.util.Enumeration;
+import java.util.Vector;
+import javax.media.j3d.*;
+import javax.vecmath.*;
+import com.sun.j3d.utils.behaviors.interpolators.*;
+import com.sun.j3d.internal.J3dUtilsI18N;
+import com.sun.j3d.loaders.ParsingErrorException;
+import com.sun.j3d.loaders.IncorrectFormatException;
+
+/**
+ * This class is responsible for parsing the data in a Scene file related to
+ * an object's animation and constructing the appropriate Java3D
+ * Behavior objects. For each keyframe defined for the animation in the
+ * Lightwave file, this class creates a LwsFrame object to parse that
+ * keyframe data and create the appropriate data structures. Then for
+ * each of those LwsFrame objects created, LwsMotion creates a knot
+ * value for a PathInterpolator and fills in the appropriate field. Finally,
+ * the class creates a RotPosScalePathInterpolator with all of the data
+ * from the animation. There are also some utility functions in this
+ * class for dealing with special cases of animations, such as animations
+ * that begin after the first frame of the scene and animations that
+ * define frames in a way that Java3D cannot easily interpret.
+ */
+
+class LwsMotion extends TextfileParser {
+
+ // data from the file
+ String motionName;
+ LwsFrame frames[];
+ int numFrames;
+ int numChannels;
+ boolean loop;
+ float totalTime;
+ int firstFrame;
+ int totalFrames;
+ Behavior behaviors;
+
+ /**
+ * Constructor
+ */
+ LwsMotion(StreamTokenizer st, int frames, float time) {
+ this(st, 0, frames, time, EXCEPTION);
+
+ }
+
+ /**
+ * Constructor: takes tokenizer, 1st frame of this animation, total
+ * number of frames, total time of animation, and the debug settings
+ */
+ LwsMotion(StreamTokenizer st, int firstFrame,
+ int frames, float time, int debugVals)
+ throws ParsingErrorException, IncorrectFormatException {
+
+ debugPrinter.setValidOutput(debugVals);
+ numFrames = 0;
+ totalTime = time;
+ this.firstFrame = firstFrame;
+ totalFrames = frames;
+ debugOutputLn(LINE_TRACE, "about to get motion name");
+ motionName = getName(st);
+ debugOutputLn(LINE_TRACE, "about to get motion");
+ getMotion(st);
+ }
+
+ /**
+ * This method parses the tokenizer and creates the data structures
+ * that hold the data from that file. For each separate keyframe,
+ * this method calls LwsFrame to parse and interpret that data.
+ */
+ void getMotion(StreamTokenizer st)
+ throws ParsingErrorException, IncorrectFormatException
+ {
+ debugOutputLn(TRACE, "getMotion()");
+ numChannels = (int)getNumber(st);
+ if (numChannels != 9) {
+ throw new IncorrectFormatException(
+ J3dUtilsI18N.getString("LwsMotion0"));
+ }
+ debugOutputLn(LINE_TRACE, "got channels");
+
+ numFrames = (int)getNumber(st);
+ frames = new LwsFrame[numFrames];
+ debugOutputLn(VALUES, "got frames" + numFrames);
+
+ for (int i = 0; i < numFrames; ++i) {
+ frames[i] = new LwsFrame(st);
+ }
+
+ debugOutput(LINE_TRACE, "got all frames");
+
+ getAndCheckString(st, "EndBehavior");
+ int repeatVal = (int)getNumber(st);
+ if (repeatVal == 1)
+ loop = false;
+ else
+ loop = true;
+
+ // need to make sure frame info is in sycn with j3d
+ // fixFrames();
+ }
+
+ /**
+ * The previous version of this method looked for sucessive frames with
+ * the same rotation value (mod 2PI). If it found such frames, it would
+ * divide that interval into 4 separate frames.
+ * This fix is not sufficient for various rotation cases, though. For
+ * instance, if the rotation difference between two frames is more than
+ * 2PI, the rotation will not case a flag to be fixed and the resulting
+ * rotation will only be between the delta of the two rotations, mod 2PI.
+ * The real fix should behave as follows:
+ * - Iterate through all sucessive frames
+ * - For any two frames that have rotation components that differ by more
+ * than PI/2 (one quarter rotation - no reason for this, but let's pick a
+ * small value to give our resulting path interpolations a better chance
+ * of behaving correctly), figure out how many frames we need to create to
+ * get increments of <= PI/2 between each frame.
+ * - Create these new frames
+ * - Set the odl frames pointer to the new frames structures.
+ */
+
+ void fixFrames() {
+
+ boolean addedFrames = false;
+ Vector newFramesList = new Vector();
+ double halfPI = (float)(Math.PI/2);
+ LwsFrame finalFrame = null;
+
+ for (int i = 1 ; i < numFrames; ++i) {
+ LwsFrame prevFrame;
+ LwsFrame lastFrame = frames[i-1];
+ LwsFrame thisFrame = frames[i];
+ LwsFrame nextFrame;
+
+ finalFrame = thisFrame;
+ newFramesList.add(lastFrame);
+
+ double largestAngleDifference = 0;
+ double thisAngle = thisFrame.getHeading();
+ double lastAngle = lastFrame.getHeading();
+ double angleDifference = Math.abs(thisAngle - lastAngle);
+ if (angleDifference > largestAngleDifference)
+ largestAngleDifference = angleDifference;
+
+ thisAngle = thisFrame.getPitch();
+ lastAngle = lastFrame.getPitch();
+ angleDifference = Math.abs(thisAngle - lastAngle);
+ if (angleDifference > largestAngleDifference)
+ largestAngleDifference = angleDifference;
+
+ thisAngle = thisFrame.getBank();
+ lastAngle = lastFrame.getBank();
+ angleDifference = Math.abs(thisAngle - lastAngle);
+ if (angleDifference > largestAngleDifference)
+ largestAngleDifference = angleDifference;
+
+ if (largestAngleDifference > halfPI) {
+ // Angles too big - create new frames
+ addedFrames = true;
+ int numNewFrames = (int)(largestAngleDifference/halfPI);
+ double increment = 1.0/(double)(numNewFrames+1);
+ double currentRatio = increment;
+
+ double totalf = frames[numFrames-1].getFrameNum();
+ double tlength = (thisFrame.getFrameNum() -
+ lastFrame.getFrameNum())/totalf;
+ double adj0;
+ double adj1;
+
+ // get the previous and next frames
+ if ((i-1) < 1) {
+ prevFrame = frames[i-1];
+ adj0 = 0.0;
+ } else {
+ prevFrame = frames[i-2];
+ adj0 = tlength/((thisFrame.getFrameNum() -
+ prevFrame.getFrameNum())/totalf);
+ }
+
+ if ((i+1) < numFrames) {
+ nextFrame = frames[i+1];
+ adj1 = tlength/((nextFrame.getFrameNum()-
+ lastFrame.getFrameNum())/totalf);
+ } else {
+ nextFrame = frames[i];
+ adj1 = 1.0;
+ }
+
+ for (int j = 0; j < numNewFrames; ++j) {
+
+ LwsFrame newFrame;
+
+ // if linear interpolation
+ if (thisFrame.linearValue == 1) {
+ newFrame = new LwsFrame(lastFrame,
+ thisFrame, currentRatio);
+
+ // if spline interpolation
+ } else {
+ newFrame = new LwsFrame(prevFrame, lastFrame,
+ thisFrame, nextFrame,
+ currentRatio, adj0, adj1);
+ }
+
+ currentRatio += increment;
+ newFramesList.add(newFrame);
+ }
+ }
+ }
+
+ // Now add in final frame
+ if (finalFrame != null)
+ newFramesList.add(finalFrame);
+ if (addedFrames) {
+
+ // Recreate frames array from newFramesList
+ LwsFrame newFrames[] = new LwsFrame[newFramesList.size()];
+ Enumeration elements = newFramesList.elements();
+ int index = 0;
+ while (elements.hasMoreElements()) {
+ newFrames[index++] = (LwsFrame)elements.nextElement();
+ }
+ frames = newFrames;
+ numFrames = frames.length;
+ for (int i = 0; i < numFrames; ++i) {
+ debugOutputLn(VALUES, "frame " + i + " = " + frames[i]);
+ frames[i].printVals();
+ }
+ }
+ }
+
+ /**
+ * Utility for getting integer mod value
+ */
+ int intMod(int divisee, int divisor) {
+ int tmpDiv = divisee;
+ int tmpDivisor = divisor;
+ if (tmpDiv < 0)
+ tmpDiv = -tmpDiv;
+ if (tmpDivisor < 0)
+ tmpDivisor = -tmpDivisor;
+ while (tmpDiv > tmpDivisor) {
+ tmpDiv -= tmpDivisor;
+ }
+ return tmpDiv;
+ }
+
+ /**
+ * Class that associates a particular frame with its effective frame
+ * number (which accounts for animations that start after frame 1)
+ */
+ class FrameHolder {
+ double frameNumber;
+ LwsFrame frame;
+
+ FrameHolder(LwsFrame theFrame, double number) {
+ frame = theFrame;
+ frameNumber = number;
+ }
+ }
+
+
+ /**
+ * This method was added to account for animations that start after
+ * the first frame (e.g., Juggler.lws starts at frame 30). We need
+ * to alter some of the information for the frames in this "frame subset"
+ */
+ void playWithFrameTimes(Vector framesVector) {
+ debugOutputLn(TRACE, "playWithFrameTimes: firstFrame = " +
+ firstFrame);
+ if (firstFrame == 1) {
+ return;
+ }
+ else if (frames[numFrames-1].getFrameNum() < totalFrames) {
+ // First, create a vector that holds all LwsFrame's in frame
+ // increasing order (where order is started at firstFrame Modulo
+ // this motion's last frame
+ int motionLastFrame =
+ (int)(frames[numFrames-1].getFrameNum() + .4999999);
+ int newFirstFrame = intMod(firstFrame, motionLastFrame);
+ int newLastFrame = intMod(totalFrames, motionLastFrame);
+ int index = 0;
+ while (index < numFrames) {
+ if (frames[index].getFrameNum() >= newFirstFrame)
+ break;
+ ++index;
+ }
+ int startIndex = index;
+ if (frames[startIndex].getFrameNum() > firstFrame &&
+ startIndex > 0)
+ startIndex--; // Actually, should interpolate
+ index = startIndex;
+ if (newFirstFrame < newLastFrame) {
+ while (index < numFrames &&
+ frames[index].getFrameNum() <= newLastFrame) {
+ FrameHolder frameHolder =
+ new FrameHolder(frames[index],
+ frames[index].getFrameNum() -
+ newFirstFrame);
+ framesVector.addElement(frameHolder);
+ ++index;
+ }
+ }
+ else {
+ double currentNewFrameNumber = -1.0;
+ while (index < numFrames) {
+ currentNewFrameNumber = frames[index].getFrameNum() -
+ newFirstFrame;
+ FrameHolder frameHolder =
+ new FrameHolder(frames[index],
+ currentNewFrameNumber);
+ framesVector.addElement(frameHolder);
+ ++index;
+ }
+ index = 0;
+ while (index <= startIndex &&
+ frames[index].getFrameNum() <= newLastFrame) {
+ if (index == 0) {
+ LwsFrame newFrame =
+ new LwsFrame(frames[index],
+ frames[index+1],
+ 1.0/(frames[index+1].getFrameNum() -
+ frames[index].getFrameNum()));
+ FrameHolder frameHolder =
+ new FrameHolder(newFrame,
+ newFrame.getFrameNum() +
+ currentNewFrameNumber);
+ framesVector.addElement(frameHolder);
+ }
+ else {
+ FrameHolder frameHolder =
+ new FrameHolder(frames[index],
+ frames[index].getFrameNum() +
+ currentNewFrameNumber);
+ framesVector.addElement(frameHolder);
+ }
+ ++index;
+ }
+ }
+ }
+ else {
+ int index = 0;
+ while (index < numFrames) {
+ if (frames[index].getFrameNum() >= firstFrame)
+ break;
+ ++index;
+ }
+ int startIndex = index;
+ if (frames[startIndex].getFrameNum() > firstFrame &&
+ startIndex > 0) {
+ // Interpolate to first frame
+ double ratio = (double)firstFrame /
+ (frames[startIndex].getFrameNum() -
+ frames[startIndex-1].getFrameNum());
+ LwsFrame newFrame = new LwsFrame(frames[startIndex-1],
+ frames[startIndex],
+ ratio);
+ FrameHolder frameHolder =
+ new FrameHolder(newFrame, newFrame.getFrameNum() -
+ firstFrame);
+ framesVector.addElement(frameHolder);
+ }
+ index = startIndex;
+ while (index < numFrames &&
+ frames[index].getFrameNum() <= totalFrames) {
+ FrameHolder frameHolder =
+ new FrameHolder(frames[index],
+ frames[index].getFrameNum() -
+ firstFrame);
+ framesVector.addElement(frameHolder);
+ ++index;
+ }
+ if (frames[index-1].getFrameNum() < totalFrames) {
+ // Interpolate to last frame
+ double ratio = (double)(totalFrames -
+ frames[index-1].getFrameNum()) /
+ (frames[index].getFrameNum() -
+ frames[index-1].getFrameNum());
+ LwsFrame newFrame = new LwsFrame(frames[index-1],
+ frames[index],
+ ratio);
+ FrameHolder frameHolder =
+ new FrameHolder(newFrame, totalFrames - firstFrame);
+ framesVector.addElement(frameHolder);
+ }
+ }
+ }
+
+ /**
+ * Normally, we just create j3d behaviors from the frames. But if the
+ * animation's first frame is after frame number one, then we have to
+ * shuffle things around to account for playing/looping on this subset
+ * of the total frames of the animation
+ */
+ void createJava3dBehaviorsForFramesSubset(TransformGroup target) {
+
+ debugOutputLn(TRACE, "createJava3dBehaviorsForFramesSubset");
+ Vector frameHolders = new Vector();
+ playWithFrameTimes(frameHolders);
+ long alphaAtOne = 0;
+
+ // determine looping
+ int loopCount;
+ if (loop)
+ loopCount = -1;
+ else
+ loopCount = 1;
+ loopCount = -1;
+
+ int numFrames = frameHolders.size();
+
+ debugOutputLn(VALUES, "totalTime = " + totalTime);
+ debugOutputLn(VALUES, "loopCount = " + loopCount);
+
+ FrameHolder lastFrameHolder =
+ (FrameHolder)frameHolders.elementAt(frameHolders.size() - 1);
+ LwsFrame lastFrame = lastFrameHolder.frame;
+ float animTime = 1000.0f * totalTime *
+ (float)(lastFrameHolder.frameNumber/(float)(totalFrames -
+ firstFrame));
+ debugOutputLn(VALUES, " anim time: " + animTime);
+ debugOutputLn(VALUES, " totalFrames = " + totalFrames);
+
+ if (!loop)
+ alphaAtOne = (long)(1000.0*totalTime - animTime);
+ Alpha theAlpha =
+ new Alpha(loopCount, Alpha.INCREASING_ENABLE,
+ 0, 0, (long)animTime, 0,
+ alphaAtOne, 0, 0, 0);
+
+ float knots[] = new float[numFrames];
+ Point3f[] positions = new Point3f[numFrames];
+ Quat4f[] quats = new Quat4f[numFrames];
+ Point3f[] scales = new Point3f[numFrames];
+ Transform3D yAxis = new Transform3D();
+ Matrix4d mat = new Matrix4d();
+ KBKeyFrame[] keyFrames = new KBKeyFrame[numFrames];
+
+ for (int i=0; i < numFrames; ++i) {
+
+ FrameHolder frameHolder = (FrameHolder)frameHolders.elementAt(i);
+ LwsFrame frame = frameHolder.frame;
+
+ // copy position
+ positions[i] = frame.getPosition();
+
+ // copy scale
+ // Used to hardcode no-scale: scales[i] = 1.0f, 1.0f, 1.0f;
+ // Note that we can't do non-uniform scaling in the current Path
+ // interpolators. The interpolator just uses the x scale.
+ // getScale makes sure that we don't have any zero scale component
+ scales[i] = frame.getScale();
+
+ // copy rotation information
+ frame.setRotationMatrix(mat);
+ debugOutputLn(VALUES, "LwsMotion::createj3dbeh, mat = " + mat);
+ quats[i] = new Quat4f();
+ quats[i].set(mat);
+ debugOutputLn(VALUES, " and quat = " + quats[i]);
+
+ // calculate knot points from frame numbers
+ if (i == 0)
+ knots[i] = 0.0f;
+ else
+ knots[i] = (float)(frameHolder.frameNumber)/
+ (float)(lastFrameHolder.frameNumber);
+
+ // Create KB key frames
+ keyFrames[i] = new KBKeyFrame(knots[i], frame.linearValue,
+ positions[i],
+ (float)frame.heading,
+ (float)frame.pitch,
+ (float)frame.bank,
+ scales[i],
+ (float)frame.tension,
+ (float)frame.continuity,
+ (float)frame.bias);
+
+ debugOutputLn(VALUES, "pos, knots, quat = " +
+ positions[i] + knots[i] + quats[i]);
+ }
+
+ // Pass the KeyFrames to the interpolator an let it do its thing
+ KBRotPosScaleSplinePathInterpolator b = new
+ KBRotPosScaleSplinePathInterpolator(theAlpha,
+ target,
+ yAxis,
+ keyFrames);
+ if (b != null) {
+ behaviors = b;
+ BoundingSphere bounds =
+ new BoundingSphere(new Point3d(0.0,0.0,0.0), 1000000.0);
+ b.setSchedulingBounds(bounds);
+ target.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
+ target.addChild(behaviors);
+ }
+ }
+
+ /**
+ * Create j3d behaviors for the data stored in this animation. This is
+ * done by creating a RotPosScalePathInterpolator object that contains
+ * all of the position, orientation, scale data for each keyframe.
+ */
+ void createJava3dBehaviors(TransformGroup target) {
+
+ if (numFrames <= 1)
+ behaviors = null;
+ else {
+ if (firstFrame > 1) {
+ createJava3dBehaviorsForFramesSubset(target);
+ return;
+ }
+
+ long alphaAtOne = 0;
+
+ // determine looping
+ int loopCount;
+ if (loop)
+ loopCount = -1;
+ else
+ loopCount = 1;
+ loopCount = -1;
+
+ debugOutputLn(VALUES, "totalTime = " + totalTime);
+ debugOutputLn(VALUES, "loopCount = " + loopCount);
+
+ float animTime = 1000.0f * totalTime *
+ (float)(frames[numFrames-1].getFrameNum()/(float)totalFrames);
+
+ debugOutputLn(VALUES, " anim time: " + animTime);
+ debugOutputLn(VALUES, " totalFrames = " + totalFrames);
+ debugOutputLn(VALUES, " lastFrame = " +
+ frames[numFrames-1].getFrameNum());
+
+ if (!loop)
+ alphaAtOne = (long)(1000.0*totalTime - animTime);
+ Alpha theAlpha =
+ new Alpha(loopCount, Alpha.INCREASING_ENABLE,
+ 0, 0, (long)animTime, 0,
+ alphaAtOne, 0, 0, 0);
+
+ float knots[] = new float[numFrames];
+ Point3f[] positions = new Point3f[numFrames];
+ Quat4f[] quats = new Quat4f[numFrames];
+ Point3f[] scales = new Point3f[numFrames];
+ Transform3D yAxis = new Transform3D();
+ Matrix4d mat = new Matrix4d();
+ KBKeyFrame[] keyFrames = new KBKeyFrame[numFrames];
+
+ for (int i=0; i < numFrames; ++i) {
+
+ // copy position
+ positions[i] = frames[i].getPosition();
+
+ // copy scale
+ // Used to hardcode no-scale: scales[i] = 1.0f, 1.0f, 1.0f;
+ // Note that we can't do non-uniform scaling in the current Path
+ // interpolators. The interpolator just uses the x scale.
+ // getScale makes sure that we don't have any 0 scale component
+ scales[i] = frames[i].getScale();
+
+ // copy rotation information
+ frames[i].setRotationMatrix(mat);
+ debugOutputLn(VALUES, "LwsMotion::createj3dbeh, mat = " + mat);
+ quats[i] = new Quat4f();
+ quats[i].set(mat);
+ debugOutputLn(VALUES, " and quat = " + quats[i]);
+
+ // calculate knot points from frame numbers
+ if (i == 0)
+ knots[i] = 0.0f;
+ else
+ knots[i] = (float)(frames[i].getFrameNum())/
+ (float)(frames[numFrames-1].getFrameNum());
+
+ // Create KB key frames
+ keyFrames[i] = new KBKeyFrame(knots[i],frames[i].linearValue,
+ positions[i],
+ (float)frames[i].heading,
+ (float)frames[i].pitch,
+ (float)frames[i].bank,
+ scales[i],
+ (float)frames[i].tension,
+ (float)frames[i].continuity,
+ (float)frames[i].bias);
+
+
+ debugOutputLn(VALUES, "pos, knots, quat = " +
+ positions[i] + knots[i] + quats[i]);
+ }
+
+ // Pass the KeyFrames to the interpolator an let it do its thing
+ KBRotPosScaleSplinePathInterpolator b = new
+ KBRotPosScaleSplinePathInterpolator(theAlpha,
+ target,
+ yAxis,
+ keyFrames);
+ if (b != null) {
+ behaviors = b;
+ BoundingSphere bounds =
+ new BoundingSphere(new Point3d(0.0,0.0,0.0), 1000000.0);
+ b.setSchedulingBounds(bounds);
+ target.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
+ target.addChild(behaviors);
+ }
+ }
+
+ }
+
+ /**
+ * Returns the Behavior object created for this animation
+ */
+ Behavior getBehaviors() {
+ return behaviors;
+ }
+
+ /**
+ * Returns the first LwsFrame object (which contains the initial
+ * setup for a given object)
+ */
+ LwsFrame getFirstFrame() {
+ if (numFrames > 0)
+ return frames[0];
+ else
+ return null;
+ }
+
+ /**
+ * Utility function for printing values
+ */
+ void printVals() {
+ debugOutputLn(VALUES, " motionName = " + motionName);
+ debugOutputLn(VALUES, " numChannels = " + numChannels);
+ debugOutputLn(VALUES, " numFrames = " + numFrames);
+ debugOutputLn(VALUES, " loop = " + loop);
+ for (int i = 0; i < numFrames; ++i) {
+ debugOutputLn(VALUES, " FRAME " + i);
+ frames[i].printVals();
+ }
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/LwsObject.java b/src/classes/share/com/sun/j3d/loaders/lw3d/LwsObject.java
new file mode 100644
index 0000000..9a4d384
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/LwsObject.java
@@ -0,0 +1,570 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+
+import java.awt.Component;
+import java.io.*;
+import java.util.Vector;
+import javax.media.j3d.*;
+import javax.vecmath.*;
+import java.util.Enumeration;
+import java.util.StringTokenizer;
+import java.util.Vector;
+import com.sun.j3d.utils.geometry.ColorCube;
+import com.sun.j3d.loaders.ParsingErrorException;
+import com.sun.j3d.loaders.IncorrectFormatException;
+import java.net.MalformedURLException;
+
+import java.net.*;
+
+/**
+ * An LwsObject is passed a handle to the text file that contains the scene
+ * and is responsible for parsing a particular section of that file that
+ * describes a single object. This section defines the type of object
+ * (either a group or some geometry specified by an Object file) and
+ * some keyframe data that describes the an animation of the
+ * orientation/position/scale of the object. For geometry objects, this
+ * class instantiates a J3dLwoParser object to parse the binary data file.
+ * For the keyframe data, the class instantiates an LwsMotion object to
+ * parse and store that data.
+ */
+
+class LwsObject extends TextfileParser implements LwsPrimitive {
+
+ // data from the file
+ String fileName;
+ String objName;
+ LwsMotion motion;
+ int parent;
+ TransformGroup objectTransform;
+ Vector objectBehavior;
+ Vector shapeList = null;
+ boolean hasPivot = false;
+ TransformGroup pivotTransGroup = null;
+
+ URL urlName;
+ String protocol;
+ int fileType;
+
+ /**
+ * Constructor: parses object section of this scene file and
+ * creates all appropriate data structures to hold the information
+ * @param st StreamTokenizer for scene file
+ * @param loadObject boolean specifying that object is not a lw3d Null
+ * object
+ * @param firstFrame int holding the first frame of the scene's animation
+ * @param totalFrames int holding the total number of frames in the scene
+ * @param totalTime float holding the total time of the animation
+ * @param loader Lw3dLoader loader object that was created by user
+ * @param debugVals in holding current debug flags
+ */
+ LwsObject(StreamTokenizer st, boolean loadObject,
+ int firstFrame, int totalFrames, float totalTime,
+ Lw3dLoader loader, int debugVals)
+ throws java.io.FileNotFoundException,
+ ParsingErrorException {
+ debugPrinter.setValidOutput(debugVals);
+ parent = -1;
+
+ fileType = loader.getFileType();
+
+ try {
+ if (loadObject) {
+ // If this is true, then the object is actually described
+ // in an external geometry file. Get that filename
+ fileName = getString(st);
+ String path = null;
+ switch (loader.getFileType()) {
+ case Lw3dLoader.FILE_TYPE_FILENAME:
+ // No other case is current implemented in this loader
+ path = loader.getBasePath();
+ if (path == null)
+ path = loader.getInternalBasePath();
+ if (path != null) {
+ // It's not sufficient to just use the base path.
+ // Lightwave scene files tend to embed path names
+ // to object files that are only correct if you
+ // start from a certain directory. For example, a
+ // scene file in data/ may point to an object in
+ // data/Objects - but in this case
+ // getInternalBasePath() would be data/, so you'd
+ // look for the object in data/data/Objects...
+ // To attempt to overcome this confusing state of
+ // affairs, let's check path/filename
+ // first, then iterate all the way up the path
+ // until there are no more members of path. This
+ // will ensure that we'll at least pick up data
+ // files if they exist off of one of the parent
+ // directories of wherever the scene file is
+ // stored.
+ // No, I don't really like this solution, but I don't
+ // know of a better general approach for now...
+
+ fileName = getQualifiedFilename(path, fileName);
+ }
+ break;
+ case Lw3dLoader.FILE_TYPE_URL:
+ path = "";
+ URL pathUrl = loader.getBaseUrl();
+ if (pathUrl != null) {
+ path = pathUrl.toString();
+ // store the protocol
+ protocol = pathUrl.getProtocol();
+ }
+ else {
+ path = loader.getInternalBaseUrl();
+ // store the protocol
+ protocol = (new URL(path)).getProtocol();
+ }
+
+ urlName = getQualifiedURL(path, fileName);
+ break;
+ }
+ }
+ else
+ // else the object is a lw3d Null object; essentially a group
+ // which contains other objects
+ objName = getString(st);
+ skip(st, "ShowObject", 2);
+ debugOutputLn(LINE_TRACE,
+ "skipped showobject, about to get objectmotion");
+ getAndCheckString(st, "ObjectMotion");
+ debugOutputLn(LINE_TRACE, "got string " + st.sval);
+ // Create an LwsMotion object to parse the animation data
+ motion = new LwsMotion(st, firstFrame, totalFrames,
+ totalTime, debugVals);
+ debugOutputLn(LINE_TRACE, "got motion");
+ boolean hasParent = false; // keeps bones prim from reassigning par
+
+ // TODO: This isn't the greatest way to stop parsing an object
+ // (keying on the ShowOptions parameter), but it seems to be valid
+ // for the current lw3d format
+ while (!isCurrentToken(st, "ShadowOptions")) {
+ if (!hasParent &&
+ isCurrentToken(st, "ParentObject")) {
+ parent = (int)getNumber(st);
+ hasParent = true;
+ }
+ else if (isCurrentToken(st, "PivotPoint")) {
+ // PivotPoint objects are tricky - they make the object
+ // rotate about this new point instead of the default
+ // So setup transform groups such that this happens
+ // correctly.
+ hasPivot = true;
+ float x = (float)getNumber(st);
+ float y = (float)getNumber(st);
+ float z = (float)getNumber(st);
+ Vector3f pivotPoint = new Vector3f(-x, -y, z);
+ Transform3D pivotTransform = new Transform3D();
+ pivotTransform.set(pivotPoint);
+ pivotTransGroup = new TransformGroup(pivotTransform);
+ pivotTransGroup.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
+ }
+
+ else if (isCurrentToken(st, "ObjDissolve")) {
+ // Just handle it for now, don't care about value
+ EnvelopeHandler env =
+ new EnvelopeHandler(st, totalFrames, totalTime);
+ }
+ st.nextToken();
+ }
+ getNumber(st); // skip shadow options parameter
+ debugOutputLn(LINE_TRACE, "done with LwsObject constructor");
+ }
+ catch (MalformedURLException e) {
+ throw new FileNotFoundException(e.getMessage());
+ }
+ catch (IOException e) {
+ throw new ParsingErrorException(e.getMessage());
+ }
+ catch (NumberFormatException e) {
+ throw new ParsingErrorException("Expected a number, got " +
+ e.getMessage());
+ }
+ }
+
+ /**
+ * This method takes the given path and filename and checks whether
+ * that file exists. If not, it chops off the last part of pathname
+ * and recurse to try again. This has the effect of searching all the
+ * way up a given pathname for the existence of the file anywhere
+ * along that path. This is somewhat of a hack to get around the
+ * constraining way that Lightwave uses to define its data object
+ * locations in its scene files.
+ *
+ * If the filename is absolute, it will use the path information from
+ * the filename first, then the path information from the lws file.
+ * If the file can not be found in these locations, the local directory
+ * will be searched.
+ * In addition, it will look for filenames converted to lowercase to
+ * make it easier to use between Windows and Unix.
+ */
+
+ String getQualifiedFilename(String pathname, String filename)
+ throws java.io.FileNotFoundException {
+
+ int index;
+ String pathname2 = "";
+
+ // System.out.println ("pathname:"+pathname+" filename:"+filename);
+
+ // Do we have an absolute filename ?
+ if (filename.indexOf (File.separator) == 0) {
+ if ((index = filename.lastIndexOf (File.separator)) != -1) {
+ pathname2 = filename.substring (0, index+1);
+ filename = filename.substring (index+1);
+ }
+ else {
+ return null; // something out of the ordinary happened
+ }
+ }
+
+ // See if we can find the file
+ // ---------------------------
+
+ // Try pathname from absolute filename
+ try {
+ if (new File(pathname2 + filename).exists()) {
+ return (pathname2 + filename);
+ }
+ }
+ catch (NullPointerException ex) {
+ ex.printStackTrace();
+ }
+ // Try lowercase filename
+ if (new File(pathname2 + filename.toLowerCase()).exists()) {
+ return (pathname2 + filename.toLowerCase());
+ }
+
+ // Try original pathname
+ if (new File(pathname + filename).exists()) {
+ return (pathname + filename);
+ }
+ // Try lowercase filename
+ if (new File(pathname + filename.toLowerCase()).exists()) {
+ return (pathname + filename.toLowerCase());
+ }
+
+ // Finally, let's check the local directory
+ if (new File(filename).exists()) {
+ return (filename);
+ }
+ // Try lowercase filename
+ if (new File(filename.toLowerCase()).exists()) {
+ return (filename.toLowerCase());
+ }
+
+ // Conditions that determine when we give up on the recursive search
+ if ((pathname.equals(File.separator)) ||
+ (pathname == null) ||
+ (pathname.equals(""))) {
+
+ throw new java.io.FileNotFoundException(filename);
+ }
+
+ // Try to find the file in the upper directories
+ // Chop off the last directory from pathname and recurse
+ StringBuffer newPathName = new StringBuffer(128);
+ StringTokenizer st = new StringTokenizer(pathname, File.separator);
+ int tokenCount = st.countTokens() - 1;
+ if (pathname.startsWith(java.io.File.separator))
+ newPathName.append(File.separator);
+ for (int i = 0; i < tokenCount; ++i) {
+ String directory = st.nextToken();
+ newPathName.append(directory);
+ newPathName.append(File.separator);
+ }
+
+ String newPath = newPathName.toString();
+ return getQualifiedFilename(newPath, filename);
+ }
+
+ URL getQualifiedURL(String path, String file)
+ throws MalformedURLException {
+
+ URL url = null;
+
+ // try the path and the file -- this is the lightwave spec
+ try {
+ // create url
+ url = new URL(path + file);
+ // see if file exists
+ url.getContent();
+ // return url if no exception is thrown
+ return url;
+ }
+ catch (IOException e) {
+ // Ignore - try something different
+ }
+
+ // try a couple other options, but trying to open connections is slow,
+ // so don't try as many options as getQualifiedFilename
+
+ // try absolute path
+ try {
+ url = new URL(file);
+ url.getContent();
+ }
+ catch (IOException ex) {
+ // Ignore - try something different
+ }
+
+ // try the absolute path with the protocol
+ try {
+ url = new URL(protocol + ":" + file);
+ url.getContent();
+ return url;
+ }
+ catch (IOException ex) {
+ // Nothing else to try so give up
+ throw new MalformedURLException(path + file);
+ }
+ }
+
+ /**
+ * Returns parent object
+ */
+ int getParent() {
+ return parent;
+ }
+
+ /**
+ * Adds the given child to the transform of this node (its parent).
+ */
+ void addChild(LwsPrimitive child) {
+ debugOutputLn(TRACE, "addChild()");
+ if (objectTransform != null) {
+ debugOutputLn(LINE_TRACE, "objectTransform = " + objectTransform);
+ if (child.getObjectNode() != null) {
+ debugOutputLn(LINE_TRACE, "child has object node");
+ if (hasPivot)
+ pivotTransGroup.addChild(child.getObjectNode());
+ else
+ objectTransform.addChild(child.getObjectNode());
+ }
+/*
+ if (child.getObjectBehaviors() != null) {
+ debugOutputLn(LINE_TRACE, "child has behaviors");
+ Group bg = child.getObjectBehaviors();
+ debugOutputLn(VALUES, " child behaviors = " + bg);
+ // TODO: should remove intermediate group nodes
+ objectBehavior.addChild(bg);
+ }
+*/
+ }
+ }
+
+ /**
+ * Creates Java3d objects from the data stored for this object.
+ * The objects created consist of: A TransformGroup that holds the
+ * transform specified by the first keyframe, a Behavior that acts
+ * on the TransformGroup if there are more than 1 keyframes, and
+ * some geometry (created by J3dLwoParser) from an external geometry
+ * file (if the object wasn't an lw3d Null object (group)).
+ */
+ void createJava3dObject(LwsObject cloneObject, int loadBehaviors)
+ throws IncorrectFormatException, ParsingErrorException,
+ FileNotFoundException
+ {
+ String seqToken = new String("_sequence_");
+ Matrix4d mat = new Matrix4d();
+ mat.setIdentity();
+ // Set the node's transform matrix according to the first frame
+ // of the object's motion
+ LwsFrame firstFrame = motion.getFirstFrame();
+ firstFrame.setMatrix(mat);
+ Transform3D t1 = new Transform3D();
+ t1.set(mat);
+ objectTransform = new TransformGroup(t1);
+ objectTransform.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
+
+ // This following bit is a hack and should probably be removed.
+ // It was put in here in order to handle the "Tloop" functionality
+ // of holosketch, which was needed for the 1998 Javaone conference
+ // (the HighNoon demo, in particular). Having the code here, or
+ // using it, means that some object file names are tagged as special
+ // because they contain the phrase "_sequence_", which tells this
+ // object file reader that that object file is, in fact, a
+ // sequence file. It then creates a SequenceReader object to
+ // read that file and create an animation from the objects defined
+ // in that file.
+ // See SequenceReader.java for more information on this.
+ // By the way, a better/fuller implementation of this functionality
+ // would involve investigating a standard Plug-In for Lightwave
+ // that allows the writing out of sequence files from Bones data.
+ // i think it would be better to base any Tloop stuff on that
+ // standard than on some proprietary hack of our own.
+
+ if (fileName != null && fileName.indexOf(seqToken) != -1) { // Tloop
+
+ int index = fileName.indexOf(seqToken);
+ index += seqToken.length();
+ String seqFilename = fileName.substring(index);
+ int endIndex = seqFilename.indexOf(".lwo");
+ if (endIndex != -1)
+ seqFilename = seqFilename.substring(0, endIndex);
+ if ((new File(seqFilename)).exists()) {
+ SequenceReader sr =
+ new SequenceReader(seqFilename,
+ motion.totalTime,
+ (int)motion.totalFrames);
+ sr.printLines();
+ sr.createJava3dObjects(debugPrinter.getValidOutput(),
+ loadBehaviors);
+ Group g = sr.getObjectNode();
+ if (g != null)
+ objectTransform.addChild(g);
+
+ // Sequence reader's getObjectBehaviors creates new Vector
+ objectBehavior = sr.getObjectBehaviors();
+
+ return;
+ }
+ }
+
+ // Okay, now that that hack is out of the way, let's get on with
+ // "normal" Lightwave object files.
+ if (fileName != null || urlName != null) {
+ // If this object refers to an obj file, load it and create
+ // geometry from it.
+ if (cloneObject == null) {
+ debugOutputLn(VALUES,
+ "About to load binary file for " + fileName);
+ // Create a J3dLwoParser object to parse the geometry file
+ // and create the appropriate geometry
+ J3dLwoParser objParser = null;
+ switch (fileType) {
+ case Lw3dLoader.FILE_TYPE_FILENAME:
+ objParser =
+ new J3dLwoParser(fileName,
+ debugPrinter.getValidOutput());
+ break;
+ case Lw3dLoader.FILE_TYPE_URL:
+ objParser = new J3dLwoParser(urlName,
+ debugPrinter.getValidOutput());
+ break;
+ }
+ objParser.createJava3dGeometry();
+ // pivot points change the parent transform
+ if (hasPivot) {
+ objectTransform.addChild(pivotTransGroup);
+ }
+ if (objParser.getJava3dShapeList() != null) {
+ shapeList = objParser.getJava3dShapeList();
+ for (Enumeration e = shapeList.elements() ;
+ e.hasMoreElements() ;) {
+ if (!hasPivot || pivotTransGroup == null)
+ objectTransform.addChild((Shape3D)e.nextElement());
+ else
+ pivotTransGroup.addChild((Shape3D)e.nextElement());
+ }
+ }
+ }
+ else {
+ // Already read that file: Clone original object
+ debugOutputLn(LINE_TRACE, "Cloning shapes");
+ Vector cloneShapeList = cloneObject.getShapeList();
+ for (Enumeration e = cloneShapeList.elements() ;
+ e.hasMoreElements() ;) {
+ debugOutputLn(LINE_TRACE, " shape clone");
+ Shape3D shape = (Shape3D)e.nextElement();
+ Shape3D cloneShape = (Shape3D)shape.cloneTree();
+ objectTransform.addChild(cloneShape);
+ }
+ }
+ }
+
+ // Create j3d behaviors for the object's animation
+ objectBehavior = new Vector();
+ if (loadBehaviors != 0) {
+ motion.createJava3dBehaviors(objectTransform);
+ Behavior b = motion.getBehaviors();
+ if (b != null)
+ objectBehavior.addElement(b);
+ }
+ }
+
+ /**
+ * Return list of Shape3D objects for this object file. This is used
+ * when cloning objects (if the scene file requests the same object file
+ * more than once, that object will be cloned instead of recreated each
+ * time).
+ */
+ Vector getShapeList() {
+ return shapeList;
+ }
+
+ /**
+ * Return the TransformGroup that holds this object file
+ */
+ public TransformGroup getObjectNode() {
+ return objectTransform;
+ }
+
+ /**
+ * Return the Group that holds this object's behaviors. The behaviors
+ * are grouped separately from the geometry so that they can be handled
+ * differently by the parent application.
+ */
+ public Vector getObjectBehaviors()
+ {
+ debugOutputLn(TRACE, "getObjectBehaviors()");
+ return objectBehavior;
+ }
+
+
+ /**
+ * Utiliy function to print some of the object values. Used in
+ * debugging.
+ */
+ void printVals()
+ {
+ debugOutputLn(VALUES, " OBJECT vals: ");
+ debugOutputLn(VALUES, " fileName = " + fileName);
+ debugOutputLn(VALUES, " objName = " + objName);
+ motion.printVals();
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/LwsPrimitive.java b/src/classes/share/com/sun/j3d/loaders/lw3d/LwsPrimitive.java
new file mode 100644
index 0000000..a0b457d
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/LwsPrimitive.java
@@ -0,0 +1,68 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+
+
+import java.util.Vector;
+import javax.media.j3d.Group;
+import javax.media.j3d.TransformGroup;
+
+/**
+ * This is an interface which is implemented by LwsObject,
+ * LwsFog, LwsBackground, LwsLight, etc. It provides a generic method
+ * for objects to access some of the common data in these classes.
+ */
+
+interface LwsPrimitive {
+
+ // interface from which other Lws types (Object, Camera, etc.)
+ // inherit methods
+
+ public Vector getObjectBehaviors();
+
+ public TransformGroup getObjectNode();
+
+}
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/ParserObject.java b/src/classes/share/com/sun/j3d/loaders/lw3d/ParserObject.java
new file mode 100644
index 0000000..1504f22
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/ParserObject.java
@@ -0,0 +1,91 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+/**
+ * This class is a superclass of the binary parsing classes. It provides
+ * some basic debugging utilities.
+ */
+
+class ParserObject {
+
+
+ final static int TRACE = DebugOutput.TRACE, VALUES = DebugOutput.VALUES;
+ final static int MISC = DebugOutput.MISC, LINE_TRACE = DebugOutput.LINE_TRACE;
+ final static int NONE = DebugOutput.NONE, EXCEPTION = DebugOutput.EXCEPTION;
+ final static int TIME = DebugOutput.TIME, WARNING = DebugOutput.WARNING;
+
+ protected DebugOutput debugPrinter;
+
+
+ ParserObject() {
+ debugPrinter = new DebugOutput(EXCEPTION);
+ }
+
+ ParserObject(int debugVals) {
+ this();
+ debugPrinter.setValidOutput(debugVals);
+ }
+
+
+ protected void debugOutputLn(int outputType, String theOutput) {
+ if (theOutput.equals(""))
+ debugPrinter.println(outputType, theOutput);
+ else
+ debugPrinter.println(outputType,
+ getClass().getName() + "::" + theOutput);
+ }
+
+ protected void debugOutput(int outputType, String theOutput) {
+ debugPrinter.print(outputType, theOutput);
+ }
+
+
+
+}
+
+
+
+
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/SequenceLine.java b/src/classes/share/com/sun/j3d/loaders/lw3d/SequenceLine.java
new file mode 100644
index 0000000..b557474
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/SequenceLine.java
@@ -0,0 +1,269 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+import java.awt.Component;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.StreamTokenizer;
+import java.io.IOException;
+import javax.media.j3d.*;
+import javax.vecmath.Point3d;
+
+import com.sun.j3d.loaders.IncorrectFormatException;
+import com.sun.j3d.loaders.ParsingErrorException;
+import java.io.FileNotFoundException;
+
+/**
+ * This class was created to handle "sequence files", which allow
+ * holosketch-type Tloop sequences to be loaded through the lw3d loader.
+ * The class reads a sequence file line by line and uses SequenceLine to
+ * load the file specified in each line.<BR>
+ * Idea behind the Tloop process:<BR>
+ * Artist creates "tloops" (animations with each keyframe's
+ * geometry saved out explicitly) where the geometries are spaced
+ * one frame apart. Then I can automatically create a SwitchValueInterpolator
+ * based on this spacing. If the number of frames in the sequence is
+ * greater than the number of frames in the tloop, then it will automatically
+ * loop until the end of the sequence.<BR>
+ * Process:<BR>
+ * 1) Artist creates an animation of a null group that has a child with some
+ * special name, such as "bucket_sequence_bucketsequence.txt.lwo", which tells
+ * the lw3d loader that it should look for a sequence file by the name of
+ * bucketsequence.txt. What happens to this object is irrelevant (as far as
+ * the loader is concerned); all animation information is taken from its
+ * parent instead.<BR>
+ * 2) Artist saves out the geometry of the bucket at whatever frames she wants
+ * to. If she's saving a tloop (a sequence of frames), she should save them
+ * under the names <filename>xxx.lwo, where xxx is the 3-digit sequence number
+ * (000, 001, 002, etc.).<BR>
+ * 3) Artist creates the sequence file, which lists all saved geometry files
+ * (except sequences - these can be referred to simply by the first file
+ * (...000.lwo)), along with their associated start/end frames. She also lists
+ * the number of files in the sequence, although this parameter is implied
+ * anyway, through the existence of the sequence files and their naming
+ * convention. Maybe we should trash this guy.<BR>
+ * 4) In the lw3d loader, when LwsObject encounters an object with the
+ * filename "..._sequence_<filename>.lwo", it searches for filename. If
+ * found, it parses the file (using the SequenceReader class) to retrieve
+ * all parameters.<BR>
+ * 5) Each SequenceLine creates a Java3D group containing its objects. This
+ * is either a plain-old-Group (if there is only one object) or a Switch group
+ * with a SwitchValueInterpolator.<BR>
+ * 6) SequenceReader constructs a Switch group and adds all SequenceLine groups
+ * to this new group. It also creates a SwitchPathInterpolator (child of
+ * PathInterolator) that contsructs an Alpha based on the startFrame values of
+ * each SequenceLine. It creates a group and adds the SwitchPathInterpolator
+ * plus any SequenceLine SwitchValueInterpolators to this group.<BR>
+ * 7) LwsObject adds the SequenceReader Switch group to its objectTransform.
+ * It does a getBehaviors() from SequenceReader and adds the result (the
+ * SwitchPathInterpolator group) to its objectBehaviors group.<BR>
+ * 8) Done.
+ */
+
+class SequenceLine {
+
+ int startFrame;
+ int endFrame;
+ String fileName;
+
+ Group geometryGroup = null;
+ Behavior behaviors;
+ int numFrames;
+ float totalTime;
+ int totalFrames;
+
+ // storedRefList keeps references to already loaded objects
+ static Hashtable storedRefList = new Hashtable();
+
+ SequenceLine(StreamTokenizer st, float time, int frames)
+ throws ParsingErrorException {
+ try {
+ totalTime = time;
+ totalFrames = frames;
+ startFrame = (int)st.nval;
+ st.nextToken();
+ endFrame = (int)st.nval;
+ st.nextToken();
+ fileName = st.sval;
+ numFrames = endFrame - startFrame + 1;
+ }
+ catch (IOException e) {
+ throw new ParsingErrorException(e.getMessage());
+ }
+ }
+
+ /**
+ * Creates a SwitchValueInterpolator which is used to switch between
+ * the objects specified for the sequence. This is done for files
+ * that end in "000", meaning that there are a sequence of files
+ * with that same base name that should specify every frame of a
+ * sequence for an object. The Switch node is used to hold all of the
+ * files and the Switch Behavior node is used to activate switching
+ * at the right time and to the right object.
+ */
+ private void createSwitchBehavior(Switch target) {
+
+ int loopCount = -1;
+ float animTime = 1000.0f * totalTime *
+ (float)(target.numChildren())/(float)totalFrames;
+ float startTime = 1000f * totalTime *
+ (float)startFrame/(float)totalFrames;
+ Alpha theAlpha =
+ new Alpha(-1, (long)startTime, 0, (long)animTime, 0, 0);
+
+ SwitchValueInterpolator b=new SwitchValueInterpolator(theAlpha,target);
+ behaviors = b;
+ BoundingSphere bounds =
+ new BoundingSphere(new Point3d(0.0,0.0,0.0), 1000000.0);
+ b.setSchedulingBounds(bounds);
+ target.setCapability(TransformGroup.ALLOW_TRANSFORM_WRITE);
+ target.addChild(behaviors);
+
+ }
+
+ /**
+ * Create Java3d objects from the data in the sequence line. This
+ * means that for a tloop file (ends in "000"), we're going to create
+ * the appropriate geometry for each file, put them all in a Switch
+ * node, then create a SwitchValueInterpolator to swap between the
+ * frames of the tloop. If it's not a tloop, then we're just going to
+ * create the geometry for that file.
+ */
+ void createJava3dObjects(int debugVals, int loadBehaviors)
+ throws IncorrectFormatException, FileNotFoundException {
+ if (fileName.indexOf("000") != -1) { // Tloop
+ int index = fileName.indexOf("000");
+ String fileNameBase = fileName.substring(0, index);
+ Switch s = new Switch();
+ s.setCapability(Switch.ALLOW_SWITCH_READ);
+ s.setCapability(Switch.ALLOW_SWITCH_WRITE);
+ String tempFileName = fileName;
+ int fileNum = 0;
+ while ((new File(tempFileName)).exists()) {
+ if (storedRefList.get(tempFileName) != null) {
+ // System.out.println("retrieve stored version of " +
+ // tempFileName);
+ SharedGroup storedGroup =
+ (SharedGroup)storedRefList.get(tempFileName);
+ Link newLink = new Link(storedGroup);
+ s.addChild(newLink);
+ }
+ else {
+ // System.out.println("reading " + tempFileName);
+ J3dLwoParser objParser = new J3dLwoParser(tempFileName,
+ debugVals);
+ objParser.createJava3dGeometry();
+ TransformGroup t = new TransformGroup();
+ SharedGroup newSharedGroup = new SharedGroup();
+ storedRefList.put(tempFileName, newSharedGroup);
+ newSharedGroup.addChild(t);
+ Link newLink = new Link(newSharedGroup);
+ s.addChild(newLink);
+ if (objParser.getJava3dShapeList() != null) {
+ for (Enumeration e =
+ objParser.getJava3dShapeList().elements() ;
+ e.hasMoreElements() ;) {
+ t.addChild((Shape3D)e.nextElement());
+ }
+ }
+ }
+ ++fileNum;
+ String fileNumString = String.valueOf(fileNum);
+ if (fileNum < 10)
+ fileNumString = "00" + fileNumString;
+ else if (fileNum < 100)
+ fileNumString = "0" + fileNumString;
+ tempFileName = fileNameBase + fileNumString + ".lwo";
+ }
+ behaviors = null;
+ if (loadBehaviors != 0) {
+ createSwitchBehavior(s);
+ }
+ geometryGroup = (Group)s;
+ }
+ else {// Not a tloop, just a file
+ geometryGroup = new Group();
+ if (storedRefList.get(fileName) != null) {
+ // System.out.println("getting old ref to " + fileName);
+ SharedGroup storedGroup =
+ (SharedGroup)storedRefList.get(fileName);
+ Link newLink = new Link(storedGroup);
+ geometryGroup.addChild(newLink);
+ }
+ else {
+ // System.out.println("reading " + fileName);
+ J3dLwoParser objParser = new J3dLwoParser(fileName,
+ debugVals);
+ objParser.createJava3dGeometry();
+ TransformGroup t = new TransformGroup();
+ if (objParser.getJava3dShapeList() != null) {
+ for (Enumeration e = objParser.getJava3dShapeList().elements() ;
+ e.hasMoreElements() ;) {
+ t.addChild((Shape3D)e.nextElement());
+ }
+ }
+ SharedGroup newSharedGroup = new SharedGroup();
+ newSharedGroup.addChild(t);
+ Link newLink = new Link(newSharedGroup);
+ geometryGroup.addChild(newLink);
+ storedRefList.put(fileName, newSharedGroup);
+ }
+ }
+ }
+
+ Group getGeometry() {
+ return geometryGroup;
+ }
+
+ Behavior getBehavior() {
+ return behaviors;
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/SequenceReader.java b/src/classes/share/com/sun/j3d/loaders/lw3d/SequenceReader.java
new file mode 100644
index 0000000..0ba239f
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/SequenceReader.java
@@ -0,0 +1,172 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+import java.awt.Component;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.StreamTokenizer;
+import java.io.IOException;
+import javax.media.j3d.*;
+import javax.vecmath.Point3d;
+
+import com.sun.j3d.loaders.IncorrectFormatException;
+import com.sun.j3d.loaders.ParsingErrorException;
+import java.io.FileNotFoundException;
+
+/**
+ * This class was created to read a special file format devised for
+ * JavaOne '98 that allowed Tloop functionality inside of Lightwave. It
+ * would be best to find a more standard solution, including using some
+ * plug-in for lw3d that I've heard of that allows artists to automatically
+ * save out the geometry for a file at every frame.
+ */
+
+class SequenceReader {
+
+
+ Vector sequenceLines;
+ float totalTime;
+ int totalFrames;
+
+ TransformGroup objectTransform;
+ Vector behaviorVector;
+
+ /**
+ * Constructor: parses a sequence file and creates a new SequenceLine
+ * object to read in every line of the file
+ */
+ SequenceReader(String filename, float time, int frames)
+ throws ParsingErrorException {
+ totalTime = time;
+ totalFrames = frames;
+ sequenceLines = new Vector();
+ try {
+ // System.out.println("reading sequence from " + filename);
+ StreamTokenizer st = new StreamTokenizer(new BufferedReader(
+ new FileReader(filename)));
+ st.wordChars('_', '_');
+ st.wordChars('/', '/');
+ int type = st.nextToken();
+ while (st.ttype != StreamTokenizer.TT_EOF) {
+ sequenceLines.addElement(new SequenceLine(st,
+ totalTime,
+ totalFrames));
+ st.nextToken();
+ }
+ }
+ catch (IOException e) {
+ throw new ParsingErrorException(e.getMessage());
+ }
+ }
+
+ /**
+ * Creates Java3D objects from the data defined in the sequence
+ * file. Calls each sequenceLine object to create its own
+ * j3d objects, then puts all of those objects in a single Switch
+ * node. Finally, it creates a SwitchPathInterpolator object which
+ * handles switching between each object/s defined by each line
+ */
+ void createJava3dObjects(int debugVals, int loadBehaviors)
+ throws FileNotFoundException {
+
+ objectTransform = new TransformGroup();
+ behaviorVector = new Vector();
+ Enumeration e = sequenceLines.elements();
+ Switch switchNode = new Switch();
+ switchNode.setCapability(Switch.ALLOW_SWITCH_READ);
+ switchNode.setCapability(Switch.ALLOW_SWITCH_WRITE);
+ objectTransform.addChild(switchNode);
+ while (e.hasMoreElements()) {
+ SequenceLine line = (SequenceLine)e.nextElement();
+ line.createJava3dObjects(debugVals, loadBehaviors);
+ if (line.getGeometry() != null)
+ switchNode.addChild(line.getGeometry());
+ //objectTransform.addChild(line.getGeometry());
+ if (line.getBehavior() != null) {
+ behaviorVector.addElement(line.getBehavior());
+ }
+ }
+ float knots[] = new float[sequenceLines.size() + 1];
+ for (int i = 0; i < knots.length-1; ++i) {
+ SequenceLine sl = (SequenceLine)sequenceLines.elementAt(i);
+ knots[i] = (float)sl.startFrame/(float)totalFrames;
+ }
+ knots[knots.length-1] = 1.0f;
+ Alpha theAlpha = new Alpha(-1, Alpha.INCREASING_ENABLE,
+ 0, 0, (long)(1000f * totalTime), 0,
+ 0, 0, 0, 0);
+
+ SwitchPathInterpolator switchPath =
+ new SwitchPathInterpolator(theAlpha,
+ knots,
+ switchNode);
+ BoundingSphere bounds =
+ new BoundingSphere(new Point3d(0.0,0.0,0.0), 1000000.0);
+ switchPath.setSchedulingBounds(bounds);
+ switchNode.addChild(switchPath);
+ behaviorVector.addElement(switchPath);
+ }
+
+ TransformGroup getObjectNode() {
+ return objectTransform;
+ }
+
+ Vector getObjectBehaviors() {
+ return behaviorVector;
+ }
+
+ void printLines() {
+ Enumeration e = sequenceLines.elements();
+ while (e.hasMoreElements()) {
+ SequenceLine line = (SequenceLine)e.nextElement();
+ }
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/ShapeHolder.java b/src/classes/share/com/sun/j3d/loaders/lw3d/ShapeHolder.java
new file mode 100644
index 0000000..38f9d66
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/ShapeHolder.java
@@ -0,0 +1,267 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+import java.util.Vector;
+import javax.vecmath.Vector3f;
+
+
+/**
+ * This class holds all of the vertex/facet/normal/surface-reference
+ * data for a particular object. It has utilities to calculate facet normals,
+ * but this is no longer in use since using the new GeomInfo utilities.
+ */
+
+class ShapeHolder extends ParserObject {
+
+
+ Vector facetSizesList;
+ Vector facetIndicesList;
+ int facetIndicesArray[];
+ int currentNumIndices = 0;
+ int numSurf;
+ int numVerts;
+ int facetIndices[];
+ int facetSizes[];
+ int normalIndices[];
+ float normalCoords[];
+ float coordsArray[];
+
+ ShapeHolder() {
+ }
+
+ ShapeHolder(int debugVals) {
+ super(debugVals);
+ }
+
+
+ /**
+ * Print out (to stdout) the geometry data (coords, indices,
+ * and facet sizes). This is a debugging utility.
+ */
+ void printGeometryData(LwoSurface surface) {
+ int i, j;
+ int indicesIndex = 0;
+ System.out.println("\nPolygon Data:");
+ System.out.println(" Surface color = " + surface.color);
+ System.out.println(" Surface diffuse = " + surface.diffuseColor);
+ for (i = 0; i < facetSizes.length; ++i) {
+ int polySize = facetSizes[i];
+ System.out.println("Facet of size " + polySize);
+ for (j = 0; j < polySize; ++j) {
+ int coordIndex = 3 * facetIndices[indicesIndex++];
+ System.out.println("x, y, z = " +
+ coordsArray[coordIndex] + ", " +
+ coordsArray[coordIndex+1] + ", " +
+ coordsArray[coordIndex+2]);
+ }
+ }
+ }
+
+ /**
+ * Constructs geometry arrays given a winding rule (it turns out that
+ * lw3d winding is opposite of j3d winding, so this is always set to
+ * true in J3dLwoParser)
+ */
+ void createArrays(boolean reverseWinding) {
+ debugOutputLn(TRACE, "createArrays()");
+ // debugOutputLn(VALUES, "facetIndices, faceSizesList = " +
+ // facetIndicesList + facetSizesList);
+ //debugOutputLn(VALUES, "ind and sizes size " +
+ // facetIndicesList.size() + ", " +
+ // facetSizesList.size());
+ //facetIndices =
+ // new int[facetIndicesList.size()];
+ facetIndices = new int[currentNumIndices];
+ if (reverseWinding) {
+ int facetBeginIndex = 0;
+ for (int facetIndex = 0;
+ facetIndex < facetSizesList.size();
+ ++facetIndex) {
+ int currFaceSize =
+ ((Integer)facetSizesList.elementAt(facetIndex)).intValue();
+ int tempFace[] = new int[currFaceSize];
+ for (int j = 0; j < currFaceSize; ++j) {
+ facetIndices[facetBeginIndex + j] =
+ facetIndicesArray[facetBeginIndex +
+ currFaceSize - j - 1];
+ }
+ facetBeginIndex += currFaceSize;
+ }
+
+ }
+ else {
+ for (int i = 0; i < facetIndices.length; ++i) {
+ facetIndices[i] = facetIndicesArray[i];
+ }
+ }
+
+ debugOutputLn(LINE_TRACE, "facetIndices.len and coordsArray.len = " +
+ facetIndices.length + ", " + coordsArray.length);
+ if (((Integer)facetSizesList.elementAt(0)).intValue() < 3) {
+ // if we're dealing with point/line primitives, then let's abandon
+ // the indexed route and simply construct a new coordsArray
+ // that holds the direct values we need for a GeometryArray
+ // object
+ debugOutputLn(LINE_TRACE, "Using direct geometry because " +
+ "facetIndices is of size " +
+ facetIndices.length +
+ " and coordsArray is of length "+
+ coordsArray.length);
+ float newCoordsArray[] = new float[facetIndices.length * 3];
+ int newCoordsIndex = 0;
+ for (int i = 0; i < facetIndices.length; ++i) {
+ newCoordsArray[newCoordsIndex++] =
+ coordsArray[facetIndices[i]*3];
+ newCoordsArray[newCoordsIndex++] =
+ coordsArray[facetIndices[i]*3+1];
+ newCoordsArray[newCoordsIndex++] =
+ coordsArray[facetIndices[i]*3+2];
+ }
+ coordsArray = newCoordsArray;
+ facetIndices = null;
+ }
+
+ facetSizes =
+ new int[facetSizesList.size()];
+ for (int i = 0; i < facetSizes.length; ++i) {
+ facetSizes[i] =
+ ((Integer)facetSizesList.elementAt(i)).intValue();
+ }
+
+ facetSizesList = null; // Force garbage collection on Vectors
+ facetIndicesList = null;
+ facetIndicesArray = null;
+ }
+
+ /**
+ * Force gc on all array objects
+ */
+ void nullify() {
+ facetSizesList = null; // Force garbage collection on everything
+ facetIndicesList = null;
+ facetIndicesArray = null;
+ facetSizes = null;
+ facetIndices = null;
+ normalCoords = null;
+ normalIndices = null;
+ }
+
+ /**
+ * This method calculates facet normals for the geometry. It is no
+ * longer used, as we're now using the GeometryInfo utility to calculate
+ * smooth normals
+ */
+ void calcNormals() {
+ debugOutputLn(TRACE, "calcNormals()");
+ debugOutputLn(LINE_TRACE, "coordsLength, facetsizes.len = " +
+ coordsArray.length + ", " + facetSizes.length);
+ if (facetSizes[0] > 2) {
+ // points and lines don't need normals, polys do
+ if (facetIndices != null) {
+ normalIndices = new int[facetIndices.length];
+ normalCoords = new float[facetIndices.length * 3];
+ }
+ else {
+ normalCoords = new float[coordsArray.length];
+ }
+ debugOutputLn(LINE_TRACE, "normalCoords, incides len = " +
+ normalCoords.length + ", " +
+ ((facetIndices == null) ? 0 : normalIndices.length));
+ int facetIndex = 0;
+ int tempIndex = -1;
+ for (int i = 0; i < facetSizes.length; i += 1) {
+ Vector3f norm;
+ int currFacetSize = facetSizes[i];
+ //debugOutputLn(LINE_TRACE, " i, facetIndex, currSize = " +
+ // i + ", " + facetIndex + ", " + currFacetSize);
+ if (currFacetSize < 3) {
+ // This shouldn't occur
+ norm = new Vector3f(0f, 0f, 1f);
+ }
+ else {
+ Vector3f v1, v2;
+ int index1, index2, index3;
+ if (facetIndices != null) {
+ index1 = facetIndices[facetIndex];
+ index2 = facetIndices[facetIndex+1];
+ index3 = facetIndices[facetIndex+2];
+ //debugOutputLn(VALUES, " index123 = " +
+ // index1 + ", " + index2 + ", " + index3);
+ }
+ else {
+ index1 = facetIndex;
+ index2 = facetIndex+1;
+ index3 = facetIndex+2;
+ }
+ v1 = new
+ Vector3f(coordsArray[index2*3] - coordsArray[index1*3],
+ coordsArray[index2*3+1] - coordsArray[index1*3+1],
+ coordsArray[index2*3+2] - coordsArray[index1*3+2]);
+ v2 = new
+ Vector3f(coordsArray[index3*3] - coordsArray[index1*3],
+ coordsArray[index3*3+1] - coordsArray[index1*3+1],
+ coordsArray[index3*3+2] - coordsArray[index1*3+2]);
+ //debugOutputLn(VALUES, "v1, v2 = " + v1 + v2);
+ norm = new Vector3f();
+ norm.cross(v1, v2);
+ norm.normalize(norm);
+ }
+
+ for (int j = 0; j < currFacetSize; ++j) {
+ int normIndex = facetIndex + j;
+ normalCoords[normIndex*3] = norm.x;
+ normalCoords[normIndex*3+1] = norm.y;
+ normalCoords[normIndex*3+2] = norm.z;
+ if (facetIndices != null)
+ normalIndices[normIndex] = normIndex;
+ }
+ facetIndex += currFacetSize;
+ }
+ }
+ debugOutputLn(TRACE, "done with calcNormals()");
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/SwitchPathInterpolator.java b/src/classes/share/com/sun/j3d/loaders/lw3d/SwitchPathInterpolator.java
new file mode 100644
index 0000000..eb167da
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/SwitchPathInterpolator.java
@@ -0,0 +1,129 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+ package com.sun.j3d.loaders.lw3d;
+
+
+import javax.vecmath.*;
+import java.util.BitSet;
+import java.util.Enumeration;
+
+import javax.media.j3d.Alpha;
+import javax.media.j3d.Node;
+import javax.media.j3d.NodeReferenceTable;
+import javax.media.j3d.SceneGraphObject;
+import javax.media.j3d.Switch;
+import com.sun.j3d.internal.J3dUtilsI18N;
+
+/**
+ * This class was used in conjunction with SequenceReader to create
+ * Tloop functionality inside of Lightwave files. This behavior handles
+ * the switching between objects defined in separate lines of a
+ * sequence file. That is, each line in a sequence file has the name
+ * of an object (or an object sequence, if the name ends in "000")
+ * and details the start and end frames that that object should be active.
+ * This class determines which object/s defined in the file should be active
+ * at any given time during the animation.
+ */
+
+class SwitchPathInterpolator extends FloatValueInterpolator {
+
+ Switch target;
+ int firstSwitchIndex;
+ int lastSwitchIndex;
+ int currentChild;
+ int childCount;
+
+ /**
+ * Constructs a new SwitchPathInterpolator object.
+ * @param alpha the alpha object for this interpolator
+ * @param knots an array of knot values that specify a spline
+ */
+ SwitchPathInterpolator(Alpha alpha, float knots[], Switch target) {
+
+ super(alpha, knots, new float[knots.length]);
+
+ if (knots.length != (target.numChildren() + 1))
+ throw new IllegalArgumentException(J3dUtilsI18N.getString("SwitchPathInterpolator0"));
+
+ this.target = target;
+ firstSwitchIndex = 0;
+ lastSwitchIndex = target.numChildren() - 1;
+ childCount = lastSwitchIndex + 1;
+ }
+
+ /**
+ * This method sets the correct child for the Switch node according
+ * to alpha
+ * @param criteria enumeration of criteria that have triggered this wakeup
+ */
+
+ public void processStimulus(Enumeration criteria) {
+
+ int child;
+
+ // Handle stimulus
+ if (this.getAlpha() != null) {
+
+ // Let PathInterpolator calculate the correct
+ // interpolated knot point
+ computePathInterpolation();
+
+ if (currentKnotIndex > 0)
+ child = currentKnotIndex - 1;
+ else
+ child = 0;
+
+ if (target.getWhichChild() != child) {
+ target.setWhichChild(child);
+ }
+
+ if ((this.getAlpha()).finished())
+ return;
+ }
+
+ wakeupOn(defaultWakeupCriterion);
+ }
+
+}
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/TargaReader.java b/src/classes/share/com/sun/j3d/loaders/lw3d/TargaReader.java
new file mode 100644
index 0000000..a96acad
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/TargaReader.java
@@ -0,0 +1,199 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferInt;
+import java.applet.Applet;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.MediaTracker;
+import java.awt.Frame;
+import java.awt.Image;
+import java.awt.image.MemoryImageSource;
+import java.awt.Toolkit;
+import java.io.FileNotFoundException;
+import java.io.DataInputStream;
+import java.io.FileInputStream;
+import java.io.BufferedInputStream;
+import com.sun.j3d.loaders.IncorrectFormatException;
+import com.sun.j3d.loaders.ParsingErrorException;
+import java.io.IOException;
+
+/**
+ * This class parses a standard Targa file and retrieves the image stored
+ * therein, storing the pixel data in a BufferedImage.
+ */
+
+class TargaReader extends ParserObject {
+
+ BufferedInputStream bufferedReader;
+ Image theImage = null;
+
+ /**
+ * Constructor: creates file reader and calls parseFile() to do the real
+ * work
+ */
+ TargaReader(String fileName, int debugVals) throws FileNotFoundException {
+ super(debugVals);
+ debugOutputLn(TRACE, "constructor");
+ bufferedReader = new BufferedInputStream(
+ new DataInputStream(new FileInputStream(fileName)));
+ if (bufferedReader != null)
+ parseFile();
+ }
+
+ /**
+ * Returns the image that was created from parsing the targa file (null
+ * if the file reading failed)
+ */
+ Image getImage() {
+ return theImage;
+ }
+
+ /**
+ * This method parses the file and stores the pixel data in a
+ * BufferedImage. The basic file format is:
+ * Byte Description
+ *
+ * 0 Image ID Length
+ * 1 Colormap type
+ * 2 Image Type
+ * 3-4 Colormap spec: 1st entry index
+ * 5-6 Colormap spec: length
+ * 7 Colormap spec: entry size
+ * 8-9 X-origin of lower-left corner
+ * 10-11 Y-origin of lower-left corner
+ * 12-13 Image width
+ * 14-15 Image height
+ * 16 Pixel depth
+ * 17 00(origin)(alpha)
+ * first 2 bytes 0, next 2 starting corner,
+ * last four number of overlay bits per pixel
+ * 18- Image ID
+ * ?? Colormap data
+ * ?? Image Data
+ * ?? Developer Area
+ * ?? Extension Area
+ * ?? File Footer
+ *
+ * We're going to make some assumptions about the format of files we're
+ * asked to load. In particular, we're not going to do any colormpa-based
+ * images: the images need to be either 24-bit or 32-bit true color.
+ * We're also going to ignore vaiours parameters in the header block, since
+ * they complicate life and don't appear to be used in Lightwave image
+ * files. In particular, the following fields will be ignored:
+ * Image ID, colormap info, xy origins, and alpha/overlay bits.
+ */
+
+ void parseFile()
+ throws IncorrectFormatException, ParsingErrorException {
+ try {
+ int idLength = bufferedReader.read();
+ int colormapPresent = bufferedReader.read();
+ int imageType = bufferedReader.read();
+ bufferedReader.skip(9); // skipping camp and xy origin data
+ int width = bufferedReader.read() | bufferedReader.read() << 8;
+ int height = bufferedReader.read() | bufferedReader.read() << 8;
+ int depth = bufferedReader.read();
+ int flags = bufferedReader.read();
+ boolean bottomToTop = ((flags & 0x20) == 0);
+ boolean leftToRight = ((flags & 0x10) == 0);
+ bufferedReader.skip(idLength);
+
+ // Check on the file parameters to see whether we should punt
+ if ((colormapPresent == 1) ||
+ imageType != 2 ||
+ (depth != 24 &&
+ depth != 32)) {
+ // Punt
+ throw new IncorrectFormatException(
+ "This format is not readable by the Lightwave " +
+ "loader. Only 24- or 32-bit true-color " +
+ "uncompressed Targa images will work");
+ }
+
+ // Image format must be okay for us to read
+ BufferedImage bImage =
+ new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+ int[] imageBits =
+ ((DataBufferInt)bImage.getRaster().getDataBuffer()).getData();
+
+ int row;
+ int column;
+
+ for (int i = 0; i < height; ++i) {
+ if (bottomToTop)
+ row = (height - i - 1);
+ else
+ row = i;
+ for (int j = 0; j < width; ++j) {
+
+ if (leftToRight)
+ column = j;
+ else
+ column = (width - j - 1);
+
+ int blue = bufferedReader.read();
+ int green = bufferedReader.read();
+ int red = bufferedReader.read();
+ int alpha = 0xff;
+ if (depth == 32)
+ alpha = bufferedReader.read();
+ imageBits[row*width + column] = alpha << 24 |
+ red << 16 |
+ green << 8 |
+ blue;
+ }
+ }
+ theImage = bImage;
+ }
+ catch (IOException e) {
+ throw new ParsingErrorException(e.getMessage());
+ }
+ }
+
+}
+
diff --git a/src/classes/share/com/sun/j3d/loaders/lw3d/TextfileParser.java b/src/classes/share/com/sun/j3d/loaders/lw3d/TextfileParser.java
new file mode 100644
index 0000000..13f9611
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/lw3d/TextfileParser.java
@@ -0,0 +1,245 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.lw3d;
+
+import java.io.*;
+import com.sun.j3d.loaders.ParsingErrorException;
+
+/**
+ * This class is a superclass for most of the Lws* Scene-file parsing
+ * classes. It provides some debugging utilities, as well as utilities for
+ * reading the types of data common to this loader.
+ */
+
+class TextfileParser {
+
+ // class variables
+ static int WORD = StreamTokenizer.TT_WORD;
+ static int NUMBER = StreamTokenizer.TT_NUMBER;
+ int currentLevel = 3;
+ final static int TRACE = DebugOutput.TRACE, VALUES = DebugOutput.VALUES;
+ final static int MISC = DebugOutput.MISC, LINE_TRACE = DebugOutput.LINE_TRACE;
+ final static int NONE = DebugOutput.NONE, EXCEPTION = DebugOutput.EXCEPTION;
+ final static int TIME = DebugOutput.TIME;
+ protected DebugOutput debugPrinter;
+ char lineSeparatorChar = 0;
+
+ TextfileParser() {
+ debugPrinter = new DebugOutput(EXCEPTION);
+ String lineSeparator = System.getProperty("line.separator");
+ lineSeparatorChar = lineSeparator.charAt(0);
+ debugOutputLn(VALUES, "lineSeparatorChar = " + (int)lineSeparatorChar);
+ }
+
+
+ protected void debugOutputLn(int outputType, String theOutput) {
+ if (theOutput.equals(""))
+ debugPrinter.println(outputType, theOutput);
+ else {
+ debugPrinter.println(outputType,
+ getClass().getName() + "::" + theOutput);
+ }
+ }
+
+ protected void debugOutput(int outputType, String theOutput) {
+ debugPrinter.print(outputType, theOutput);
+ }
+
+ /**
+ * Utility method to advance the tokenizer until we see the given
+ * string. This is used to skip by various parameters that we
+ * currently ignore in the loader.
+ */
+ void skipUntilString(StreamTokenizer st, String theString)
+ throws ParsingErrorException {
+ boolean done = false;
+ try {
+ while (!done) {
+ st.nextToken();
+ if (st.ttype == WORD &&
+ st.sval.equals(theString))
+ done = true;
+ }
+ }
+ catch (IOException e) {
+ throw new ParsingErrorException(e.getMessage());
+ }
+ }
+
+
+ /**
+ * Returns number from the tokenizer. Note that we don't recognize
+ * numbers in the tokenizer automatically because numbers might be in
+ * scientific notation, which isn't processed correctly by
+ * StreamTokenizer
+ */
+ double getNumber(StreamTokenizer st)
+ throws ParsingErrorException, NumberFormatException {
+ try {
+ int token = st.nextToken();
+ }
+ catch (IOException e) {
+ throw new ParsingErrorException(e.getMessage());
+ }
+ checkType(st, WORD);
+ return ((Double.valueOf(st.sval)).doubleValue());
+ }
+
+ /**
+ * Returns String from the tokenizer
+ */
+ String getString(StreamTokenizer st) throws ParsingErrorException {
+ try {
+ st.nextToken();
+ }
+ catch (IOException e) {
+ throw new ParsingErrorException(e.getMessage());
+ }
+ checkType(st, WORD);
+ return (st.sval);
+ }
+
+ /**
+ * Returns a "name" from the stream. This is different from simply a
+ * String because the name could contain whitespace characters
+ * (such as "object 1" or "objectname (sequence)") that would confuse
+ * the string parser. So we just grab all characters until EOL and
+ * concatenate them together to form the name
+ */
+ String getName(StreamTokenizer st) throws ParsingErrorException {
+ String theName = "";
+ st.ordinaryChar(lineSeparatorChar);
+ st.ordinaryChar('\n');
+ st.ordinaryChar('\r');
+ try {
+ st.nextToken();
+ while (st.ttype != lineSeparatorChar &&
+ st.ttype != '\r' &&
+ st.ttype != '\n') {
+ if (st.ttype != '(' &&
+ st.ttype != ')')
+ theName += st.sval;
+ st.nextToken();
+ }
+ }
+ catch (IOException e) {
+ throw new ParsingErrorException(e.getMessage());
+ }
+ st.whitespaceChars(lineSeparatorChar, lineSeparatorChar);
+ st.whitespaceChars('\n', '\n');
+ st.whitespaceChars('\r', '\r');
+ debugOutputLn(VALUES, "name = " + theName);
+ return theName;
+ }
+
+ /**
+ * Gets the next token and ensures that it is the string we were
+ * expecting to see
+ */
+ void getAndCheckString(StreamTokenizer st, String expectedValue)
+ throws ParsingErrorException {
+ try {
+ st.nextToken();
+ checkString(st, expectedValue);
+ }
+ catch (IOException e) {
+ throw new ParsingErrorException(e.getMessage());
+ }
+ }
+
+ /**
+ * Error checking routine - makes sure the current token is the string
+ * we were expecting
+ */
+ void checkString(StreamTokenizer st, String theString) throws
+ ParsingErrorException {
+ if (!(st.ttype == StreamTokenizer.TT_WORD) ||
+ !st.sval.equals(theString))
+ throw new ParsingErrorException(
+ "Bad String Token (wanted " + theString + ", got " + st.sval +
+ ": " + st.toString());
+ }
+
+ /**
+ * Error checking routine - makes sure the current token is of the right
+ * type
+ */
+ void checkType(StreamTokenizer st, int theType)
+ throws ParsingErrorException {
+ if (!(st.ttype == theType))
+ throw new ParsingErrorException(
+ "Bad Type Token, Expected " + theType + " and received" +
+ st.ttype);
+ }
+
+ /**
+ * Utility routine - gets next token, checks it against our expectation,
+ * then skips a given number of tokens. This can be used to parse
+ * through (and ignore) certain parameter/value sets in the files
+ */
+ void skip(StreamTokenizer st, String tokenString, int skipVals)
+ throws ParsingErrorException {
+ try {
+ st.nextToken();
+ checkString(st, tokenString);
+ for (int i = 0; i < skipVals; ++i) {
+ st.nextToken();
+ }
+ }
+ catch (IOException e) {
+ throw new ParsingErrorException(e.getMessage());
+ }
+ }
+
+ /**
+ * Utility method- used to check whether the current token is equal
+ * to the given string
+ */
+ boolean isCurrentToken(StreamTokenizer st, String tokenString) {
+ if (st.ttype == WORD)
+ return (st.sval.equals(tokenString));
+ return false;
+ }
+}
diff --git a/src/classes/share/com/sun/j3d/loaders/objectfile/DefaultMaterials.java b/src/classes/share/com/sun/j3d/loaders/objectfile/DefaultMaterials.java
new file mode 100644
index 0000000..b573272
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/objectfile/DefaultMaterials.java
@@ -0,0 +1,952 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.objectfile;
+
+/**
+ * This class provides default materials for the object file loader
+ */
+class DefaultMaterials {
+ /**
+ * String that describes the default materials.
+ */
+ static final String materials =
+ "newmtl amber\n" +
+ "Ka 0.0531 0.0531 0.0531\n" +
+ "Kd 0.5755 0.2678 0.0000\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 60.0000\n" +
+ "\n" +
+ "newmtl amber_trans\n" +
+ "Ka 0.0531 0.0531 0.0531\n" +
+ "Kd 0.5755 0.2678 0.0000\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "d 0.8400\n" +
+ "Ns 60.0000\n" +
+ "\n" +
+ "newmtl charcoal\n" +
+ "Ka 0.0082 0.0082 0.0082\n" +
+ "Kd 0.0041 0.0041 0.0041\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 60.0000\n" +
+ "\n" +
+ "newmtl lavendar\n" +
+ "Ka 0.1281 0.0857 0.2122\n" +
+ "Kd 0.2187 0.0906 0.3469\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 60.0000\n" +
+ "\n" +
+ "newmtl navy_blue\n" +
+ "Ka 0.0000 0.0000 0.0490\n" +
+ "Kd 0.0000 0.0000 0.0531\n" +
+ "Ks 0.1878 0.1878 0.1878\n" +
+ "illum 2\n" +
+ "Ns 91.4700\n" +
+ "\n" +
+ "newmtl pale_green\n" +
+ "Ka 0.0444 0.0898 0.0447\n" +
+ "Kd 0.0712 0.3796 0.0490\n" +
+ "Ks 0.1878 0.1878 0.1878\n" +
+ "illum 2\n" +
+ "Ns 91.4700\n" +
+ "\n" +
+ "newmtl pale_pink\n" +
+ "Ka 0.0898 0.0444 0.0444\n" +
+ "Kd 0.6531 0.2053 0.4160\n" +
+ "Ks 0.1878 0.1878 0.1878\n" +
+ "illum 2\n" +
+ "Ns 91.4700\n" +
+ "\n" +
+ "newmtl pale_yellow\n" +
+ "Ka 0.3606 0.3755 0.0935\n" +
+ "Kd 0.6898 0.6211 0.1999\n" +
+ "Ks 0.1878 0.1878 0.1878\n" +
+ "illum 2\n" +
+ "Ns 91.4700\n" +
+ "\n" +
+ "newmtl peach\n" +
+ "Ka 0.3143 0.1187 0.0167\n" +
+ "Kd 0.6367 0.1829 0.0156\n" +
+ "Ks 0.1878 0.1878 0.1878\n" +
+ "illum 2\n" +
+ "Ns 91.4700\n" +
+ "\n" +
+ "newmtl periwinkle\n" +
+ "Ka 0.0000 0.0000 0.1184\n" +
+ "Kd 0.0000 0.0396 0.8286\n" +
+ "Ks 0.1878 0.1878 0.1878\n" +
+ "illum 2\n" +
+ "Ns 91.4700\n" +
+ "\n" +
+ "newmtl redwood\n" +
+ "Ka 0.0204 0.0027 0.0000\n" +
+ "Kd 0.2571 0.0330 0.0000\n" +
+ "Ks 0.1878 0.1878 0.1878\n" +
+ "illum 2\n" +
+ "Ns 91.4700\n" +
+ "\n" +
+ "newmtl smoked_glass\n" +
+ "Ka 0.0000 0.0000 0.0000\n" +
+ "Kd 0.0041 0.0041 0.0041\n" +
+ "Ks 0.1878 0.1878 0.1878\n" +
+ "illum 2\n" +
+ "d 0.9800\n" +
+ "Ns 91.4700\n" +
+ "\n" +
+ "newmtl aqua_filter\n" +
+ "Ka 0.0000 0.0000 0.0000\n" +
+ "Kd 0.3743 0.6694 0.5791\n" +
+ "Ks 0.1878 0.1878 0.1878\n" +
+ "illum 2\n" +
+ "d 0.9800\n" +
+ "Ns 91.4700\n" +
+ "\n" +
+ "newmtl yellow_green\n" +
+ "Ka 0.0000 0.0000 0.0000\n" +
+ "Kd 0.1875 0.4082 0.0017\n" +
+ "Ks 0.1878 0.1878 0.1878\n" +
+ "illum 2\n" +
+ "Ns 91.4700\n" +
+ "\n" +
+ "newmtl bluetint\n" +
+ "Ka 0.1100 0.4238 0.5388\n" +
+ "Kd 0.0468 0.7115 0.9551\n" +
+ "Ks 0.3184 0.3184 0.3184\n" +
+ "illum 9\n" +
+ "d 0.5700\n" +
+ "Ns 60.0000\n" +
+ "sharpness 60.0000\n" +
+ "\n" +
+ "newmtl plasma\n" +
+ "Ka 0.4082 0.0816 0.2129\n" +
+ "Kd 1.0000 0.0776 0.4478\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 9\n" +
+ "d 0.7500\n" +
+ "Ns 60.0000\n" +
+ "sharpness 60.0000\n" +
+ "\n" +
+ "newmtl emerald\n" +
+ "Ka 0.0470 1.0000 0.0000\n" +
+ "Kd 0.0470 1.0000 0.0000\n" +
+ "Ks 0.2000 0.2000 0.2000\n" +
+ "illum 9\n" +
+ "d 0.7500\n" +
+ "Ns 60.0000\n" +
+ "sharpness 60.0000\n" +
+ "\n" +
+ "newmtl ruby\n" +
+ "Ka 1.0000 0.0000 0.0000\n" +
+ "Kd 1.0000 0.0000 0.0000\n" +
+ "Ks 0.2000 0.2000 0.2000\n" +
+ "illum 9\n" +
+ "d 0.7500\n" +
+ "Ns 60.0000\n" +
+ "sharpness 60.0000\n" +
+ "\n" +
+ "newmtl sapphire\n" +
+ "Ka 0.0235 0.0000 1.0000\n" +
+ "Kd 0.0235 0.0000 1.0000\n" +
+ "Ks 0.2000 0.2000 0.2000\n" +
+ "illum 9\n" +
+ "d 0.7500\n" +
+ "Ns 60.0000\n" +
+ "sharpness 60.0000\n" +
+ "\n" +
+ "newmtl white\n" +
+ "Ka 0.4000 0.4000 0.4000\n" +
+ "Kd 1.0000 1.0000 1.0000\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 60.0000\n" +
+ "\n" +
+ "newmtl red\n" +
+ "Ka 0.4449 0.0000 0.0000\n" +
+ "Kd 0.7714 0.0000 0.0000\n" +
+ "Ks 0.8857 0.0000 0.0000\n" +
+ "illum 2\n" +
+ "Ns 136.4300\n" +
+ "\n" +
+ "newmtl blue_pure\n" +
+ "Ka 0.0000 0.0000 0.5000\n" +
+ "Kd 0.0000 0.0000 1.0000\n" +
+ "Ks 0.0000 0.0000 0.5000\n" +
+ "illum 2\n" +
+ "Ns 65.8900\n" +
+ "\n" +
+ "newmtl lime\n" +
+ "Ka 0.0000 0.5000 0.0000\n" +
+ "Kd 0.0000 1.0000 0.0000\n" +
+ "Ks 0.0000 0.5000 0.0000\n" +
+ "illum 2\n" +
+ "Ns 65.8900\n" +
+ "\n" +
+ "newmtl green\n" +
+ "Ka 0.0000 0.2500 0.0000\n" +
+ "Kd 0.0000 0.2500 0.0000\n" +
+ "Ks 0.0000 0.2500 0.0000\n" +
+ "illum 2\n" +
+ "Ns 65.8900\n" +
+ "\n" +
+ "newmtl yellow\n" +
+ "Ka 1.0000 0.6667 0.0000\n" +
+ "Kd 1.0000 0.6667 0.0000\n" +
+ "Ks 1.0000 0.6667 0.0000\n" +
+ "illum 2\n" +
+ "Ns 65.8900\n" +
+ "\n" +
+ "newmtl purple\n" +
+ "Ka 0.5000 0.0000 1.0000\n" +
+ "Kd 0.5000 0.0000 1.0000\n" +
+ "Ks 0.5000 0.0000 1.0000\n" +
+ "illum 2\n" +
+ "Ns 65.8900\n" +
+ "\n" +
+ "newmtl orange\n" +
+ "Ka 1.0000 0.1667 0.0000\n" +
+ "Kd 1.0000 0.1667 0.0000\n" +
+ "Ks 1.0000 0.1667 0.0000\n" +
+ "illum 2\n" +
+ "Ns 65.8900\n" +
+ "\n" +
+ "newmtl grey\n" +
+ "Ka 0.5000 0.5000 0.5000\n" +
+ "Kd 0.1837 0.1837 0.1837\n" +
+ "Ks 0.5000 0.5000 0.5000\n" +
+ "illum 2\n" +
+ "Ns 65.8900\n" +
+ "\n" +
+ "newmtl rubber\n" +
+ "Ka 0.0000 0.0000 0.0000\n" +
+ "Kd 0.0100 0.0100 0.0100\n" +
+ "Ks 0.1000 0.1000 0.1000\n" +
+ "illum 2\n" +
+ "Ns 65.8900\n" +
+ "\n" +
+ "newmtl flaqua\n" +
+ "Ka 0.0000 0.4000 0.4000\n" +
+ "Kd 0.0000 0.5000 0.5000\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl flblack\n" +
+ "Ka 0.0000 0.0000 0.0000\n" +
+ "Kd 0.0041 0.0041 0.0041\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl flblue_pure\n" +
+ "Ka 0.0000 0.0000 0.5592\n" +
+ "Kd 0.0000 0.0000 0.7102\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl flgrey\n" +
+ "Ka 0.2163 0.2163 0.2163\n" +
+ "Kd 0.5000 0.5000 0.5000\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl fllime\n" +
+ "Ka 0.0000 0.3673 0.0000\n" +
+ "Kd 0.0000 1.0000 0.0000\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl florange\n" +
+ "Ka 0.6857 0.1143 0.0000\n" +
+ "Kd 1.0000 0.1667 0.0000\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl flpurple\n" +
+ "Ka 0.2368 0.0000 0.4735\n" +
+ "Kd 0.3755 0.0000 0.7510\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl flred\n" +
+ "Ka 0.4000 0.0000 0.0000\n" +
+ "Kd 1.0000 0.0000 0.0000\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl flyellow\n" +
+ "Ka 0.7388 0.4925 0.0000\n" +
+ "Kd 1.0000 0.6667 0.0000\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl pink\n" +
+ "Ka 0.9469 0.0078 0.2845\n" +
+ "Kd 0.9878 0.1695 0.6702\n" +
+ "Ks 0.7429 0.2972 0.2972\n" +
+ "illum 2\n" +
+ "Ns 106.2000\n" +
+ "\n" +
+ "newmtl flbrown\n" +
+ "Ka 0.0571 0.0066 0.0011\n" +
+ "Kd 0.1102 0.0120 0.0013\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl brown\n" +
+ "Ka 0.1020 0.0185 0.0013\n" +
+ "Kd 0.0857 0.0147 0.0000\n" +
+ "Ks 0.1633 0.0240 0.0000\n" +
+ "illum 2\n" +
+ "Ns 65.8900\n" +
+ "\n" +
+ "newmtl glass\n" +
+ "Ka 1.0000 1.0000 1.0000\n" +
+ "Kd 0.4873 0.4919 0.5306\n" +
+ "Ks 0.6406 0.6939 0.9020\n" +
+ "illum 2\n" +
+ "Ns 200.0000\n" +
+ "\n" +
+ "newmtl flesh\n" +
+ "Ka 0.4612 0.3638 0.2993\n" +
+ "Kd 0.5265 0.4127 0.3374\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 60.0000\n" +
+ "\n" +
+ "newmtl aqua\n" +
+ "Ka 0.0000 0.4000 0.4000\n" +
+ "Kd 0.0000 0.5000 0.5000\n" +
+ "Ks 0.5673 0.5673 0.5673\n" +
+ "illum 2\n" +
+ "Ns 60.0000\n" +
+ "\n" +
+ "newmtl black\n" +
+ "Ka 0.0000 0.0000 0.0000\n" +
+ "Kd 0.0020 0.0020 0.0020\n" +
+ "Ks 0.5184 0.5184 0.5184\n" +
+ "illum 2\n" +
+ "Ns 157.3600\n" +
+ "\n" +
+ "newmtl silver\n" +
+ "Ka 0.9551 0.9551 0.9551\n" +
+ "Kd 0.6163 0.6163 0.6163\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 60.0000\n" +
+ "\n" +
+ "newmtl dkblue_pure\n" +
+ "Ka 0.0000 0.0000 0.0449\n" +
+ "Kd 0.0000 0.0000 0.1347\n" +
+ "Ks 0.0000 0.0000 0.5673\n" +
+ "illum 2\n" +
+ "Ns 65.8900\n" +
+ "\n" +
+ "newmtl fldkblue_pure\n" +
+ "Ka 0.0000 0.0000 0.0449\n" +
+ "Kd 0.0000 0.0000 0.1347\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl dkgreen\n" +
+ "Ka 0.0000 0.0122 0.0000\n" +
+ "Kd 0.0058 0.0245 0.0000\n" +
+ "Ks 0.0000 0.0490 0.0000\n" +
+ "illum 2\n" +
+ "Ns 60.0000\n" +
+ "\n" +
+ "newmtl dkgrey\n" +
+ "Ka 0.0490 0.0490 0.0490\n" +
+ "Kd 0.0490 0.0490 0.0490\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 60.0000\n" +
+ "\n" +
+ "newmtl ltbrown\n" +
+ "Ka 0.1306 0.0538 0.0250\n" +
+ "Kd 0.2776 0.1143 0.0531\n" +
+ "Ks 0.3000 0.1235 0.0574\n" +
+ "illum 2\n" +
+ "Ns 60.0000\n" +
+ "\n" +
+ "newmtl fldkgreen\n" +
+ "Ka 0.0000 0.0122 0.0000\n" +
+ "Kd 0.0058 0.0245 0.0000\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl flltbrown\n" +
+ "Ka 0.1306 0.0538 0.0250\n" +
+ "Kd 0.2776 0.1143 0.0531\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl tan\n" +
+ "Ka 0.4000 0.3121 0.1202\n" +
+ "Kd 0.6612 0.5221 0.2186\n" +
+ "Ks 0.5020 0.4118 0.2152\n" +
+ "illum 2\n" +
+ "Ns 60.0000\n" +
+ "\n" +
+ "newmtl fltan\n" +
+ "Ka 0.4000 0.3121 0.1202\n" +
+ "Kd 0.6612 0.4567 0.1295\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl brzskin\n" +
+ "Ka 0.4408 0.2694 0.1592\n" +
+ "Kd 0.3796 0.2898 0.2122\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 25.0000\n" +
+ "\n" +
+ "newmtl lips\n" +
+ "Ka 0.4408 0.2694 0.1592\n" +
+ "Kd 0.9265 0.2612 0.2898\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 25.0000\n" +
+ "\n" +
+ "newmtl redorange\n" +
+ "Ka 0.3918 0.0576 0.0000\n" +
+ "Kd 0.7551 0.0185 0.0000\n" +
+ "Ks 0.4694 0.3224 0.1667\n" +
+ "illum 2\n" +
+ "Ns 132.5600\n" +
+ "\n" +
+ "newmtl blutan\n" +
+ "Ka 0.4408 0.2694 0.1592\n" +
+ "Kd 0.0776 0.2571 0.2041\n" +
+ "Ks 0.1467 0.1469 0.0965\n" +
+ "illum 2\n" +
+ "Ns 25.0000\n" +
+ "\n" +
+ "newmtl bluteal\n" +
+ "Ka 0.0041 0.1123 0.1224\n" +
+ "Kd 0.0776 0.2571 0.2041\n" +
+ "Ks 0.1467 0.1469 0.0965\n" +
+ "illum 2\n" +
+ "Ns 25.0000\n" +
+ "\n" +
+ "newmtl pinktan\n" +
+ "Ka 0.4408 0.2694 0.1592\n" +
+ "Kd 0.6857 0.2571 0.2163\n" +
+ "Ks 0.1467 0.1469 0.0965\n" +
+ "illum 2\n" +
+ "Ns 25.0000\n" +
+ "\n" +
+ "newmtl brnhair\n" +
+ "Ka 0.0612 0.0174 0.0066\n" +
+ "Kd 0.0898 0.0302 0.0110\n" +
+ "Ks 0.1306 0.0819 0.0352\n" +
+ "illum 2\n" +
+ "Ns 60.4700\n" +
+ "\n" +
+ "newmtl blondhair\n" +
+ "Ka 0.4449 0.2632 0.0509\n" +
+ "Kd 0.5714 0.3283 0.0443\n" +
+ "Ks 0.7755 0.4602 0.0918\n" +
+ "illum 2\n" +
+ "Ns 4.6500\n" +
+ "\n" +
+ "newmtl flblonde\n" +
+ "Ka 0.4449 0.2632 0.0509\n" +
+ "Kd 0.5714 0.3283 0.0443\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl yelloworng\n" +
+ "Ka 0.5837 0.1715 0.0000\n" +
+ "Kd 0.8857 0.2490 0.0000\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 60.0000\n" +
+ "\n" +
+ "newmtl bone\n" +
+ "Ka 0.3061 0.1654 0.0650\n" +
+ "Kd 0.9000 0.7626 0.4261\n" +
+ "Ks 0.8939 0.7609 0.5509\n" +
+ "illum 2\n" +
+ "Ns 200.0000\n" +
+ "\n" +
+ "newmtl teeth\n" +
+ "Ka 0.6408 0.5554 0.3845\n" +
+ "Kd 0.9837 0.7959 0.4694\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl brass\n" +
+ "Ka 0.2490 0.1102 0.0000\n" +
+ "Kd 0.4776 0.1959 0.0000\n" +
+ "Ks 0.5796 0.5796 0.5796\n" +
+ "illum 2\n" +
+ "Ns 134.8800\n" +
+ "\n" +
+ "newmtl dkred\n" +
+ "Ka 0.0939 0.0000 0.0000\n" +
+ "Kd 0.2286 0.0000 0.0000\n" +
+ "Ks 0.2490 0.0000 0.0000\n" +
+ "illum 2\n" +
+ "Ns 60.0000\n" +
+ "\n" +
+ "newmtl taupe\n" +
+ "Ka 0.1061 0.0709 0.0637\n" +
+ "Kd 0.2041 0.1227 0.1058\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 84.5000\n" +
+ "\n" +
+ "newmtl dkteal\n" +
+ "Ka 0.0000 0.0245 0.0163\n" +
+ "Kd 0.0000 0.0653 0.0449\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 55.0400\n" +
+ "\n" +
+ "newmtl dkdkgrey\n" +
+ "Ka 0.0000 0.0000 0.0000\n" +
+ "Kd 0.0122 0.0122 0.0122\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 60.0000\n" +
+ "\n" +
+ "newmtl dkblue\n" +
+ "Ka 0.0000 0.0029 0.0408\n" +
+ "Kd 0.0000 0.0041 0.0571\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 60.0000\n" +
+ "\n" +
+ "newmtl gold\n" +
+ "Ka 0.7224 0.1416 0.0000\n" +
+ "Kd 1.0000 0.4898 0.0000\n" +
+ "Ks 0.7184 0.3695 0.3695\n" +
+ "illum 2\n" +
+ "Ns 123.2600\n" +
+ "\n" +
+ "newmtl redbrick\n" +
+ "Ka 0.1102 0.0067 0.0067\n" +
+ "Kd 0.3306 0.0398 0.0081\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl flmustard\n" +
+ "Ka 0.4245 0.2508 0.0000\n" +
+ "Kd 0.8898 0.3531 0.0073\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl flpinegreen\n" +
+ "Ka 0.0367 0.0612 0.0204\n" +
+ "Kd 0.1061 0.2163 0.0857\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl fldkred\n" +
+ "Ka 0.0939 0.0000 0.0000\n" +
+ "Kd 0.2286 0.0082 0.0082\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl fldkgreen2\n" +
+ "Ka 0.0025 0.0122 0.0014\n" +
+ "Kd 0.0245 0.0694 0.0041\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl flmintgreen\n" +
+ "Ka 0.0408 0.1429 0.0571\n" +
+ "Kd 0.1306 0.2898 0.1673\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl olivegreen\n" +
+ "Ka 0.0167 0.0245 0.0000\n" +
+ "Kd 0.0250 0.0367 0.0000\n" +
+ "Ks 0.2257 0.2776 0.1167\n" +
+ "illum 2\n" +
+ "Ns 97.6700\n" +
+ "\n" +
+ "newmtl skin\n" +
+ "Ka 0.2286 0.0187 0.0187\n" +
+ "Kd 0.1102 0.0328 0.0139\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 17.8300\n" +
+ "\n" +
+ "newmtl redbrown\n" +
+ "Ka 0.1469 0.0031 0.0000\n" +
+ "Kd 0.2816 0.0060 0.0000\n" +
+ "Ks 0.3714 0.3714 0.3714\n" +
+ "illum 2\n" +
+ "Ns 141.0900\n" +
+ "\n" +
+ "newmtl deepgreen\n" +
+ "Ka 0.0000 0.0050 0.0000\n" +
+ "Kd 0.0000 0.0204 0.0050\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 113.1800\n" +
+ "\n" +
+ "newmtl flltolivegreen\n" +
+ "Ka 0.0167 0.0245 0.0000\n" +
+ "Kd 0.0393 0.0531 0.0100\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl jetflame\n" +
+ "Ka 0.7714 0.0000 0.0000\n" +
+ "Kd 0.9510 0.4939 0.0980\n" +
+ "Ks 0.8531 0.5222 0.0000\n" +
+ "illum 2\n" +
+ "Ns 132.5600\n" +
+ "\n" +
+ "newmtl brownskn\n" +
+ "Ka 0.0122 0.0041 0.0000\n" +
+ "Kd 0.0204 0.0082 0.0000\n" +
+ "Ks 0.0735 0.0508 0.0321\n" +
+ "illum 2\n" +
+ "Ns 20.1600\n" +
+ "\n" +
+ "newmtl greenskn\n" +
+ "Ka 0.0816 0.0449 0.0000\n" +
+ "Kd 0.0000 0.0735 0.0000\n" +
+ "Ks 0.0490 0.1224 0.0898\n" +
+ "illum 3\n" +
+ "Ns 46.5100\n" +
+ "sharpness 146.5100\n" +
+ "\n" +
+ "newmtl ltgrey\n" +
+ "Ka 0.5000 0.5000 0.5000\n" +
+ "Kd 0.3837 0.3837 0.3837\n" +
+ "Ks 0.5000 0.5000 0.5000\n" +
+ "illum 2\n" +
+ "Ns 65.8900\n" +
+ "\n" +
+ "newmtl bronze\n" +
+ "Ka 0.0449 0.0204 0.0000\n" +
+ "Kd 0.0653 0.0367 0.0122\n" +
+ "Ks 0.0776 0.0408 0.0000\n" +
+ "illum 3\n" +
+ "Ns 137.2100\n" +
+ "sharpness 125.5800\n" +
+ "\n" +
+ "newmtl bone1\n" +
+ "Ka 0.6408 0.5554 0.3845\n" +
+ "Kd 0.9837 0.7959 0.4694\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl flwhite1\n" +
+ "Ka 0.9306 0.9306 0.9306\n" +
+ "Kd 1.0000 1.0000 1.0000\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl flwhite\n" +
+ "Ka 0.6449 0.6116 0.5447\n" +
+ "Kd 0.9837 0.9309 0.8392\n" +
+ "Ks 0.8082 0.7290 0.5708\n" +
+ "illum 2\n" +
+ "Ns 200.0000\n" +
+ "\n" +
+ "newmtl shadow\n" +
+ "Kd 0.0350 0.0248 0.0194\n" +
+ "illum 0\n" +
+ "d 0.7500\n" +
+ "\n" +
+ "newmtl fldkolivegreen\n" +
+ "Ka 0.0056 0.0082 0.0000\n" +
+ "Kd 0.0151 0.0204 0.0038\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl fldkdkgrey\n" +
+ "Ka 0.0000 0.0000 0.0000\n" +
+ "Kd 0.0122 0.0122 0.0122\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl lcdgreen\n" +
+ "Ka 0.4000 0.4000 0.4000\n" +
+ "Kd 0.5878 1.0000 0.5061\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 60.0000\n" +
+ "\n" +
+ "newmtl brownlips\n" +
+ "Ka 0.1143 0.0694 0.0245\n" +
+ "Kd 0.1429 0.0653 0.0408\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 25.0000\n" +
+ "\n" +
+ "newmtl muscle\n" +
+ "Ka 0.2122 0.0077 0.0154\n" +
+ "Kd 0.4204 0.0721 0.0856\n" +
+ "Ks 0.1184 0.1184 0.1184\n" +
+ "illum 2\n" +
+ "Ns 25.5800\n" +
+ "\n" +
+ "newmtl flltgrey\n" +
+ "Ka 0.5224 0.5224 0.5224\n" +
+ "Kd 0.8245 0.8245 0.8245\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl offwhite.warm\n" +
+ "Ka 0.5184 0.4501 0.3703\n" +
+ "Kd 0.8367 0.6898 0.4490\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 60.0000\n" +
+ "\n" +
+ "newmtl offwhite.cool\n" +
+ "Ka 0.5184 0.4501 0.3703\n" +
+ "Kd 0.8367 0.6812 0.5703\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 60.0000\n" +
+ "\n" +
+ "newmtl yellowbrt\n" +
+ "Ka 0.4000 0.4000 0.4000\n" +
+ "Kd 1.0000 0.7837 0.0000\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 60.0000\n" +
+ "\n" +
+ "newmtl chappie\n" +
+ "Ka 0.4000 0.4000 0.4000\n" +
+ "Kd 0.5837 0.1796 0.0367\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 60.0000\n" +
+ "\n" +
+ "newmtl archwhite\n" +
+ "Ka 0.2816 0.2816 0.2816\n" +
+ "Kd 0.9959 0.9959 0.9959\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl archwhite2\n" +
+ "Ka 0.2816 0.2816 0.2816\n" +
+ "Kd 0.8408 0.8408 0.8408\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl lighttan\n" +
+ "Ka 0.0980 0.0536 0.0220\n" +
+ "Kd 0.7020 0.4210 0.2206\n" +
+ "Ks 0.8286 0.8057 0.5851\n" +
+ "illum 2\n" +
+ "Ns 177.5200\n" +
+ "\n" +
+ "newmtl lighttan2\n" +
+ "Ka 0.0980 0.0492 0.0144\n" +
+ "Kd 0.3143 0.1870 0.0962\n" +
+ "Ks 0.8286 0.8057 0.5851\n" +
+ "illum 2\n" +
+ "Ns 177.5200\n" +
+ "\n" +
+ "newmtl lighttan3\n" +
+ "Ka 0.0980 0.0492 0.0144\n" +
+ "Kd 0.1796 0.0829 0.0139\n" +
+ "Ks 0.8286 0.8057 0.5851\n" +
+ "illum 2\n" +
+ "Ns 177.5200\n" +
+ "\n" +
+ "newmtl lightyellow\n" +
+ "Ka 0.5061 0.1983 0.0000\n" +
+ "Kd 1.0000 0.9542 0.3388\n" +
+ "Ks 1.0000 0.9060 0.0000\n" +
+ "illum 2\n" +
+ "Ns 177.5200\n" +
+ "\n" +
+ "newmtl lighttannew\n" +
+ "Ka 0.0980 0.0492 0.0144\n" +
+ "Kd 0.7878 0.6070 0.3216\n" +
+ "Ks 0.8286 0.8057 0.5851\n" +
+ "illum 2\n" +
+ "Ns 177.5200\n" +
+ "\n" +
+ "newmtl default\n" +
+ "Ka 0.4000 0.4000 0.4000\n" +
+ "Kd 0.7102 0.7020 0.6531\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 128.0000\n" +
+ "\n" +
+ "newmtl ship2\n" +
+ "Ka 0.0000 0.0000 0.0000\n" +
+ "Kd 1.0000 1.0000 1.0000\n" +
+ "Ks 0.1143 0.1143 0.1143\n" +
+ "illum 2\n" +
+ "Ns 60.0000\n" +
+ "\n" +
+ "newmtl dkpurple\n" +
+ "Ka 0.0082 0.0000 0.0163\n" +
+ "Kd 0.0245 0.0000 0.0490\n" +
+ "Ks 0.1266 0.0000 0.2531\n" +
+ "illum 2\n" +
+ "Ns 65.8900\n" +
+ "\n" +
+ "newmtl dkorange\n" +
+ "Ka 0.4041 0.0123 0.0000\n" +
+ "Kd 0.7143 0.0350 0.0000\n" +
+ "Ks 0.7102 0.0870 0.0000\n" +
+ "illum 2\n" +
+ "Ns 65.8900\n" +
+ "\n" +
+ "newmtl mintgrn\n" +
+ "Ka 0.0101 0.1959 0.0335\n" +
+ "Kd 0.0245 0.4776 0.0816\n" +
+ "Ks 0.0245 0.4776 0.0816\n" +
+ "illum 2\n" +
+ "Ns 65.8900\n" +
+ "\n" +
+ "newmtl fgreen\n" +
+ "Ka 0.0000 0.0449 0.0000\n" +
+ "Kd 0.0000 0.0449 0.0004\n" +
+ "Ks 0.0062 0.0694 0.0000\n" +
+ "illum 2\n" +
+ "Ns 106.2000\n" +
+ "\n" +
+ "newmtl glassblutint\n" +
+ "Ka 0.4000 0.4000 0.4000\n" +
+ "Kd 0.5551 0.8000 0.7730\n" +
+ "Ks 0.7969 0.9714 0.9223\n" +
+ "illum 4\n" +
+ "d 0.3300\n" +
+ "Ns 60.0000\n" +
+ "sharpness 60.0000\n" +
+ "\n" +
+ "newmtl bflesh\n" +
+ "Ka 0.0122 0.0122 0.0122\n" +
+ "Kd 0.0245 0.0081 0.0021\n" +
+ "Ks 0.0531 0.0460 0.0153\n" +
+ "illum 2\n" +
+ "Ns 20.1600\n" +
+ "\n" +
+ "newmtl meh\n" +
+ "Ka 0.4000 0.4000 0.4000\n" +
+ "Kd 0.5551 0.8000 0.7730\n" +
+ "Ks 0.7969 0.9714 0.9223\n" +
+ "illum 4\n" +
+ "d 0.7500\n" +
+ "Ns 183.7200\n" +
+ "sharpness 60.0000\n" +
+ "\n" +
+ "newmtl violet\n" +
+ "Ka 0.0083 0.0000 0.1265\n" +
+ "Kd 0.0287 0.0269 0.1347\n" +
+ "Ks 0.2267 0.4537 0.6612\n" +
+ "illum 2\n" +
+ "Ns 96.9000\n" +
+ "\n" +
+ "newmtl iris\n" +
+ "Ka 0.3061 0.0556 0.0037\n" +
+ "Kd 0.0000 0.0572 0.3184\n" +
+ "Ks 0.8041 0.6782 0.1477\n" +
+ "illum 2\n" +
+ "Ns 188.3700\n" +
+ "\n" +
+ "newmtl blugrn\n" +
+ "Ka 0.4408 0.4144 0.1592\n" +
+ "Kd 0.0811 0.6408 0.2775\n" +
+ "Ks 0.1467 0.1469 0.0965\n" +
+ "illum 2\n" +
+ "Ns 25.0000\n" +
+ "\n" +
+ "newmtl glasstransparent\n" +
+ "Ka 0.2163 0.2163 0.2163\n" +
+ "Kd 0.4694 0.4694 0.4694\n" +
+ "Ks 0.6082 0.6082 0.6082\n" +
+ "illum 4\n" +
+ "d 0.7500\n" +
+ "Ns 200.0000\n" +
+ "sharpness 60.0000\n" +
+ "\n" +
+ "newmtl fleshtransparent\n" +
+ "Ka 0.4000 0.2253 0.2253\n" +
+ "Kd 0.6898 0.2942 0.1295\n" +
+ "Ks 0.7388 0.4614 0.4614\n" +
+ "illum 4\n" +
+ "d 0.7500\n" +
+ "Ns 6.2000\n" +
+ "sharpness 60.0000\n" +
+ "\n" +
+ "newmtl fldkgrey\n" +
+ "Ka 0.0449 0.0449 0.0449\n" +
+ "Kd 0.0939 0.0939 0.0939\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl sky_blue\n" +
+ "Ka 0.1363 0.2264 0.4122\n" +
+ "Kd 0.1241 0.5931 0.8000\n" +
+ "Ks 0.0490 0.0490 0.0490\n" +
+ "illum 2\n" +
+ "Ns 13.9500\n" +
+ "\n" +
+ "newmtl fldkpurple\n" +
+ "Ka 0.0443 0.0257 0.0776\n" +
+ "Kd 0.1612 0.0000 0.3347\n" +
+ "Ks 0.0000 0.0000 0.0000\n" +
+ "illum 2\n" +
+ "Ns 13.9500\n" +
+ "\n" +
+ "newmtl dkbrown\n" +
+ "Ka 0.0143 0.0062 0.0027\n" +
+ "Kd 0.0087 0.0038 0.0016\n" +
+ "Ks 0.2370 0.2147 0.1821\n" +
+ "illum 3\n" +
+ "Ns 60.0000\n" +
+ "sharpness 60.0000\n" +
+ "\n" +
+ "newmtl bone2\n" +
+ "Ka 0.6408 0.5388 0.3348\n" +
+ "Kd 0.9837 0.8620 0.6504\n" +
+ "illum 1\n" +
+ "\n" +
+ "newmtl bluegrey\n" +
+ "Ka 0.4000 0.4000 0.4000\n" +
+ "Kd 0.1881 0.2786 0.2898\n" +
+ "Ks 0.3000 0.3000 0.3000\n" +
+ "illum 2\n" +
+ "Ns 14.7300\n" +
+ "\n" +
+ "newmtl metal\n" +
+ "Ka 0.9102 0.8956 0.1932\n" +
+ "Kd 0.9000 0.7626 0.4261\n" +
+ "Ks 0.8939 0.8840 0.8683\n" +
+ "illum 2\n" +
+ "Ns 200.0000\n" +
+ "\n" +
+ "newmtl sand_stone\n" +
+ "Ka 0.1299 0.1177 0.0998\n" +
+ "Kd 0.1256 0.1138 0.0965\n" +
+ "Ks 0.2370 0.2147 0.1821\n" +
+ "illum 3\n" +
+ "Ns 60.0000\n" +
+ "sharpness 60.0000\n" +
+ "\n" +
+ "newmtl hair\n" +
+ "Ka 0.0013 0.0012 0.0010\n" +
+ "Kd 0.0008 0.0007 0.0006\n" +
+ "Ks 0.0000 0.0000 0.0000\n" +
+ "illum 3\n" +
+ "Ns 60.0000\n" +
+ "sharpness 60.0000\n";
+}
diff --git a/src/classes/share/com/sun/j3d/loaders/objectfile/ObjectFile.java b/src/classes/share/com/sun/j3d/loaders/objectfile/ObjectFile.java
new file mode 100644
index 0000000..d3fcd67
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/objectfile/ObjectFile.java
@@ -0,0 +1,1334 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.objectfile;
+
+import com.sun.j3d.loaders.Scene;
+import com.sun.j3d.loaders.SceneBase;
+import com.sun.j3d.loaders.Loader;
+import com.sun.j3d.loaders.IncorrectFormatException;
+import com.sun.j3d.loaders.ParsingErrorException;
+import com.sun.j3d.loaders.objectfile.ObjectFileParser;
+import com.sun.j3d.loaders.objectfile.ObjectFileMaterials;
+import com.sun.j3d.utils.geometry.GeometryInfo;
+import com.sun.j3d.utils.geometry.NormalGenerator;
+import com.sun.j3d.utils.geometry.Stripifier;
+import java.io.FileNotFoundException;
+import java.io.StreamTokenizer;
+import java.io.Reader;
+import java.io.BufferedReader;
+import java.io.BufferedInputStream;
+import java.io.FileReader;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.HashMap;
+import java.util.StringTokenizer;
+import javax.media.j3d.*;
+import javax.vecmath.Color3f;
+import javax.vecmath.Point3f;
+import javax.vecmath.Vector3f;
+import javax.vecmath.TexCoord2f;
+import java.net.MalformedURLException;
+
+
+
+/**
+ * The ObjectFile class implements the Loader interface for the Wavefront
+ * .obj file format, a standard 3D object file format created for use with
+ * Wavefront's Advanced Visualizer (tm) and available for purchase from
+ * Viewpoint DataLabs, as well as other 3D model companies. Object Files
+ * are text based
+ * files supporting both polygonal and free-form geometry (curves
+ * and surfaces). The Java 3D .obj file loader supports a subset of the
+ * file format, but it is enough to load almost all commonly available
+ * Object Files. Free-form geometry is not supported.</p>
+ *
+ * The Object File tokens currently supported by this loader are:</p>
+ * <code>v <i>float</i> <i>float</i> <i>float</i></code></p>
+ * <dl><dd>A single vertex's geometric position in space. The first vertex
+ * listed in the file has index 1,
+ * and subsequent vertices are numbered sequentially.</dl></p>
+ * <code>vn <i>float</i> <i>float</i> <i>float</i></code></p>
+ * <dl><dd>A normal. The first normal in the file is index 1, and
+ * subsequent normals are numbered sequentially.</dl></p>
+ * <code>vt <i>float</i> <i>float</i></code></p>
+ * <dl><dd>A texture coordinate. The first texture coordinate in the file is
+ * index 1, and subsequent normals are numbered sequentially.</dl></p>
+ * <code>f <i>int</i> <i>int</i> <i>int</i> . . .</code></p>
+ * <dl><dd><i><b>or</b></i></dl></p>
+ * <code>f <i>int</i>/<i>int</i> <i>int</i>/<i>int</i> <i>int</i>/<i>int</i> . . .</code></p>
+ * <dl><dd><i><b>or</i></b></dl></p>
+ * <code>f <i>int</i>/<i>int</i>/<i>int</i> <i>int</i>/<i>int</i>/<i>int</i> <i>int</i>/<i>int</i>/<i>int</i> . . .</code></p>
+ * <dl><dd>A polygonal face. The numbers are indexes into the arrays of
+ * vertex positions, texture coordinates, and normals respectively.
+ * There is no maximum number of vertices that a single polygon may
+ * contain. The .obj file specification says that each face must
+ * be flat and convex, but if the TRIANGULATE flag is sent to the
+ * ObjectFile constructor, each face will be triangulated by the
+ * Java 3D Triangulator, and therefore may be concave.
+ * A number may be omitted if, for example, texture coordinates are
+ * not being defined in the model. Numbers are normally positive
+ * indexes, but may also be negative. An index of -1 means the last
+ * member added to the respective array, -2 is the one before that,
+ * and so on.</dl></p>
+ * <code>g <i>name</i></code></p>
+ * <dl><dd>Faces defined after this token will be added to the named group.
+ * These geometry groups are returned as separated Shape3D objects
+ * attached to the parent SceneGroup. Each named Shape3D will also
+ * be in the Hashtable returned by Scene.getNamedObjects(). It is
+ * legal to add faces to a group, switch to another group, and then
+ * add more faces to the original group by reissuing the same name
+ * with the g token. If faces are added to the model before the g
+ * token is seen, the faces are put into the default group called
+ * "default."</dl></p>
+ * <code>s <i>int</i></code></p>
+ * <dl><dd><i><b>or</i></b></dl></p>
+ * <code>s off</code></p>
+ * <dl><dd>If the vn token is not used in the file to specify vertex normals
+ * for the model, this token may be used to put faces into groups
+ * for normal calculation ("smoothing groups") in the same manner as
+ * the 'g' token
+ * is used to group faces geometrically. Faces in the same smoothing
+ * group will have their normals calculated as if they are part of
+ * the same smooth surface. To do this, we use the Java 3D NormalGenerator
+ * utility with the creaseAngle parameter set to PI (180 degrees -
+ * smooth shading, no creases) or to whatever the user has set the
+ * creaseAngle. Faces in group 0 or 'off' use a
+ * creaseAngle of zero, meaning there is no smoothing (the normal
+ * of the face is used at all vertices giving the surface a faceted
+ * look; there will be a
+ * crease, or "Hard Edge," between each face in group zero). There is
+ * also an implied hard edge <i>between</i> each smoothing group, where they
+ * meet each other.</p>
+ * </p>
+ * If neither the vn nor the s token is used in the file, then normals
+ * are calculated using the creaseAngle set in the contructor.
+ * Normals are calculated on each geometry
+ * group separately, meaning there will be a hard edge between each
+ * geometry group.</dl></p>
+ * </p>
+ * <code>usemtl <i>name</i></code></p>
+ * <dl><dd>The current (and subsequent) geometry groups (specified with
+ * the 'g' token) have applied
+ * to them the named material property. The following set of material
+ * properties are available by default:</dl></p>
+ * <pre>
+ * amber amber_trans aqua aqua_filter
+ * archwhite archwhite2 bflesh black
+ * blondhair blue_pure bluegrey bluetint
+ * blugrn blutan bluteal bone
+ * bone1 bone2 brass brnhair
+ * bronze brown brownlips brownskn
+ * brzskin chappie charcoal deepgreen
+ * default dkblue dkblue_pure dkbrown
+ * dkdkgrey dkgreen dkgrey dkorange
+ * dkpurple dkred dkteal emerald
+ * fgreen flaqua flblack flblonde
+ * flblue_pure flbrown fldkblue_pure fldkdkgrey
+ * fldkgreen fldkgreen2 fldkgrey fldkolivegreen
+ * fldkpurple fldkred flesh fleshtransparent
+ * flgrey fllime flltbrown flltgrey
+ * flltolivegreen flmintgreen flmustard florange
+ * flpinegreen flpurple flred fltan
+ * flwhite flwhite1 flyellow glass
+ * glassblutint glasstransparent gold green
+ * greenskn grey hair iris
+ * jetflame lavendar lcdgreen lighttan
+ * lighttan2 lighttan3 lighttannew lightyellow
+ * lime lips ltbrown ltgrey
+ * meh metal mintgrn muscle
+ * navy_blue offwhite.cool offwhite.warm olivegreen
+ * orange pale_green pale_pink pale_yellow
+ * peach periwinkle pink pinktan
+ * plasma purple red redbrick
+ * redbrown redorange redwood rubber
+ * ruby sand_stone sapphire shadow
+ * ship2 silver skin sky_blue
+ * smoked_glass tan taupe teeth
+ * violet white yellow yellow_green
+ * yellowbrt yelloworng
+ * </pre>
+ * <code>mtllib <i>filename</i></code></p>
+ * <dl><dd>Load material properties from the named file. Materials
+ * with the same name as the predefined materials above will override
+ * the default value. Any directory path information in (filename)
+ * is ignored. The .mtl files are assumed to be in the same directory
+ * as the .obj file. If they are in a different directory, use
+ * Loader.setBasePath() (or Loader.setBaseUrl() ). The format of the
+ * material properties files
+ * are as follows:</p>
+ * <code>newmtl <i>name</i></code></p>
+ * <dl><dd>Start the definition of a new named material property.</dl></p>
+ * <code>Ka <i>float</i> <i>float</i> <i>float</i></code></p>
+ * <dl><dd>Ambient color.</dl></p>
+ * <code>Kd <i>float</i> <i>float</i> <i>float</i></code></p>
+ * <dl><dd>Diffuse color.</dl></p>
+ * <code>Ks <i>float</i> <i>float</i> <i>float</i></code></p>
+ * <dl><dd>Specular color.</dl></p>
+ * <code>illum <i>(0, 1, or 2)</i></code></p>
+ * <dl><dd>0 to disable lighting, 1 for ambient & diffuse only (specular
+ * color set to black), 2 for full lighting.</dl></p>
+ * <code>Ns <i>float</i></code></p>
+ * <dl><dd>Shininess (clamped to 1.0 - 128.0).</dl></p>
+ * <code>map_Kd <i>filename</i></code></p>
+ * <dl><dd>Texture map. Supports .rgb, .rgba, .int, .inta, .sgi, and
+ * .bw files in addition to those supported by
+ * <a href="../../utils/image/TextureLoader.html">TextureLoader</a>.
+ * </dl></dl></p>
+ */
+
+public class ObjectFile implements Loader {
+ // 0=Input file assumed good
+ // 1=Input file checked for inconsistencies
+ // 2=path names
+ // 4=flags
+ // 8=Timing Info
+ // 16=Tokens
+ // 32=Token details (use with 16)
+ // 64=limits of model coordinates
+ private static final int DEBUG = 1;
+
+ /**
+ * Flag sent to constructor. The object's vertices will be changed
+ * so that the object is centered at (0,0,0) and the coordinate
+ * positions are all in the range of (-1,-1,-1) to (1,1,1).
+ */
+ public static final int RESIZE = LOAD_SOUND_NODES << 1;
+
+ /**
+ * Flag sent to constructor. The Shape3D object will be created
+ * by using the GeometryInfo POLYGON_ARRAY primitive, causing
+ * them to be Triangulated by GeometryInfo. Use
+ * this if you suspect concave or other non-behaving polygons
+ * in your model.
+ */
+ public static final int TRIANGULATE = RESIZE << 1;
+
+ /**
+ * Flag sent to constructor. Use if the vertices in your .obj
+ * file were specified with clockwise winding (Java 3D wants
+ * counter-clockwise) so you see the back of the polygons and
+ * not the front. Calls GeometryInfo.reverse().
+ */
+ public static final int REVERSE = TRIANGULATE << 1;
+
+ /**
+ * Flag sent to contructor. After normals are generated the data
+ * will be analyzed to find triangle strips. Use this if your
+ * hardware supports accelerated rendering of strips.
+ */
+ public static final int STRIPIFY = REVERSE << 1;
+
+ private static final char BACKSLASH = '\\';
+
+ private int flags;
+ private String basePath = null;
+ private URL baseUrl = null;
+ private boolean fromUrl = false;
+ private float radians;
+
+ // First, lists of points are read from the .obj file into these arrays. . .
+ private ArrayList coordList; // Holds Point3f
+ private ArrayList texList; // Holds TexCoord2f
+ private ArrayList normList; // Holds Vector3f
+
+ // . . . and index lists are read into these arrays.
+ private ArrayList coordIdxList; // Holds Integer index into coordList
+ private ArrayList texIdxList; // Holds Integer index into texList
+ private ArrayList normIdxList; // Holds Integer index into normList
+
+ // The length of each face is stored in this array.
+ private ArrayList stripCounts; // Holds Integer
+
+ // Each face's Geometry Group membership is kept here. . .
+ private HashMap groups; // key=Integer index into stripCounts
+ // value=String name of group
+ private String curGroup;
+
+ // . . . and Smoothing Group membership is kept here
+ private HashMap sGroups; // key=Integer index into stripCounts
+ // value=String name of group
+ private String curSgroup;
+
+ // The name of each group's "usemtl" material property is kept here
+ private HashMap groupMaterials; // key=String name of Group
+ // value=String name of material
+
+
+ // After reading the entire file, the faces are converted into triangles.
+ // The Geometry Group information is converted into these structures. . .
+ private HashMap triGroups; // key=String name of group
+ // value=ArrayList of Integer
+ // indices into coordIdxList
+ private ArrayList curTriGroup;
+
+ // . . . and Smoothing Group info is converted into these.
+ private HashMap triSgroups; // key=String name of group
+ // value=ArrayList of Integer
+ // indices into coordIdxList
+ private ArrayList curTriSgroup;
+
+
+ // Finally, coordList, texList, and normList are converted to arrays for
+ // use with GeometryInfo
+ private Point3f coordArray[] = null;
+ private Vector3f normArray[] = null;
+ private TexCoord2f texArray[] = null;
+
+ // Used for debugging
+ private long time;
+
+ private ObjectFileMaterials materials = null;
+
+
+ void readVertex(ObjectFileParser st) throws ParsingErrorException {
+ Point3f p = new Point3f();
+
+ st.getNumber();
+ p.x = (float)st.nval;
+ st.getNumber();
+ p.y = (float)st.nval;
+ st.getNumber();
+ p.z = (float)st.nval;
+
+ if ((DEBUG & 32) != 0)
+ System.out.println(" (" + p.x + "," + p.y + "," + p.z + ")");
+
+ st.skipToNextLine();
+
+ // Add this vertex to the array
+ coordList.add(p);
+ } // End of readVertex
+
+
+ /**
+ * readNormal
+ */
+ void readNormal(ObjectFileParser st) throws ParsingErrorException {
+ Vector3f p = new Vector3f();
+
+ st.getNumber();
+ p.x = (float)st.nval;
+ st.getNumber();
+ p.y = (float)st.nval;
+ st.getNumber();
+ p.z = (float)st.nval;
+
+ if ((DEBUG & 32) != 0)
+ System.out.println(" (" + p.x + "," + p.y + "," + p.z + ")");
+
+ st.skipToNextLine();
+
+ // Add this vertex to the array
+ normList.add(p);
+ } // End of readNormal
+
+
+ /**
+ * readTexture
+ */
+ void readTexture(ObjectFileParser st) throws ParsingErrorException {
+ TexCoord2f p = new TexCoord2f();
+
+ st.getNumber();
+ p.x = (float)st.nval;
+ st.getNumber();
+ p.y = (float)st.nval;
+
+ if ((DEBUG & 32) != 0)
+ System.out.println(" (" + p.x + "," + p.y + ")");
+
+ st.skipToNextLine();
+
+ // Add this vertex to the array
+ texList.add(p);
+ } // End of readTexture
+
+
+ /**
+ * readFace
+ *
+ * Adds the indices of the current face to the arrays.
+ *
+ * ViewPoint files can have up to three arrays: Vertex Positions,
+ * Texture Coordinates, and Vertex Normals. Each vertex can
+ * contain indices into all three arrays.
+ */
+ void readFace(ObjectFileParser st) throws ParsingErrorException {
+ int vertIndex, texIndex = 0, normIndex = 0;
+ int count = 0;
+
+ // There are n vertices on each line. Each vertex is comprised
+ // of 1-3 numbers separated by slashes ('/'). The slashes may
+ // be omitted if there's only one number.
+
+ st.getToken();
+
+ while (st.ttype != st.TT_EOL) {
+ // First token is always a number (or EOL)
+ st.pushBack();
+ st.getNumber();
+ vertIndex = (int)st.nval - 1;
+ if (vertIndex < 0) vertIndex += coordList.size() + 1;
+ coordIdxList.add(new Integer(vertIndex));
+
+ // Next token is a slash, a number, or EOL. Continue on slash
+ st.getToken();
+ if (st.ttype == '/') {
+
+ // If there's a number after the first slash, read it
+ st.getToken();
+ if (st.ttype == st.TT_WORD) {
+ // It's a number
+ st.pushBack();
+ st.getNumber();
+ texIndex = (int)st.nval - 1;
+ if (texIndex < 0) texIndex += texList.size() + 1;
+ texIdxList.add(new Integer(texIndex));
+ st.getToken();
+ }
+
+ // Next token is a slash, a number, or EOL. Continue on slash
+ if (st.ttype == '/') {
+
+ // There has to be a number after the 2nd slash
+ st.getNumber();
+ normIndex = (int)st.nval - 1;
+ if (normIndex < 0) normIndex += normList.size() + 1;
+ normIdxList.add(new Integer(normIndex));
+ st.getToken();
+ }
+ }
+ if ((DEBUG & 32) != 0) {
+ System.out.println(" " + vertIndex + '/' + texIndex +
+ '/' + normIndex);
+ }
+ count++;
+ }
+
+ Integer faceNum = new Integer(stripCounts.size());
+ stripCounts.add(new Integer(count));
+
+ // Add face to current groups
+ groups.put(faceNum, curGroup);
+ if (curSgroup != null) sGroups.put(faceNum, curSgroup);
+
+ // In case we exited early
+ st.skipToNextLine();
+ } // End of readFace
+
+
+ /**
+ * readPartName
+ */
+ void readPartName(ObjectFileParser st) {
+ st.getToken();
+
+ // Find the Material Property of the current group
+ String curMat = (String)groupMaterials.get(curGroup);
+
+ // New faces will be added to the curGroup
+ if (st.ttype != ObjectFileParser.TT_WORD) curGroup = "default";
+ else curGroup = st.sval;
+ if ((DEBUG & 32) != 0) System.out.println(" Changed to group " + curGroup);
+
+ // See if this group has Material Properties yet
+ if (groupMaterials.get(curGroup) == null) {
+ // It doesn't - carry over from last group
+ groupMaterials.put(curGroup, curMat);
+ }
+
+ st.skipToNextLine();
+ } // End of readPartName
+
+
+ /**
+ * readMaterialName
+ */
+ void readMaterialName(ObjectFileParser st) throws ParsingErrorException {
+ st.getToken();
+ if (st.ttype == ObjectFileParser.TT_WORD) {
+ groupMaterials.put(curGroup, new String(st.sval));
+ if ((DEBUG & 32) != 0) {
+ System.out.println(" Material Property " + st.sval +
+ " assigned to group " + curGroup);
+ }
+ }
+ st.skipToNextLine();
+ } // End of readMaterialName
+
+
+ /**
+ * loadMaterialFile
+ *
+ * Both types of slashes are returned as tokens from our parser,
+ * so we go through the line token by token and keep just the
+ * last token on the line. This should be the filename without
+ * any directory info.
+ */
+ void loadMaterialFile(ObjectFileParser st) throws ParsingErrorException {
+ String s = null;
+
+ // Filenames are case sensitive
+ st.lowerCaseMode(false);
+
+ // Get name of material file (skip path)
+ do {
+ st.getToken();
+ if (st.ttype == ObjectFileParser.TT_WORD) s = st.sval;
+ } while (st.ttype != ObjectFileParser.TT_EOL);
+
+ materials.readMaterialFile(fromUrl,
+ fromUrl ? baseUrl.toString() : basePath, s);
+
+ st.lowerCaseMode(true);
+ st.skipToNextLine();
+ } // End of loadMaterialFile
+
+
+ /**
+ * readSmoothingGroup
+ */
+ void readSmoothingGroup(ObjectFileParser st) throws ParsingErrorException {
+ st.getToken();
+ if (st.ttype != ObjectFileParser.TT_WORD) {
+ st.skipToNextLine();
+ return;
+ }
+ if (st.sval.equals("off")) curSgroup = "0";
+ else curSgroup = st.sval;
+ if ((DEBUG & 32) != 0) System.out.println(" Smoothing group " + curSgroup);
+ st.skipToNextLine();
+ } // End of readSmoothingGroup
+
+
+ /**
+ * readFile
+ *
+ * Read the model data from the file.
+ */
+ void readFile(ObjectFileParser st) throws ParsingErrorException {
+ int t;
+
+ st.getToken();
+ while (st.ttype != ObjectFileParser.TT_EOF) {
+
+ // Print out one token for each line
+ if ((DEBUG & 16) != 0) {
+ System.out.print("Token ");
+ if (st.ttype == ObjectFileParser.TT_EOL) System.out.println("EOL");
+ else if (st.ttype == ObjectFileParser.TT_WORD)
+ System.out.println(st.sval);
+ else System.out.println((char)st.ttype);
+ }
+
+ if (st.ttype == ObjectFileParser.TT_WORD) {
+ if (st.sval.equals("v")) {
+ readVertex(st);
+ } else if (st.sval.equals("vn")) {
+ readNormal(st);
+ } else if (st.sval.equals("vt")) {
+ readTexture(st);
+ } else if (st.sval.equals("f")) {
+ readFace(st);
+ } else if (st.sval.equals("fo")) { // Not sure what the dif is
+ readFace(st);
+ } else if (st.sval.equals("g")) {
+ readPartName(st);
+ } else if (st.sval.equals("s")) {
+ readSmoothingGroup(st);
+ } else if (st.sval.equals("p")) {
+ st.skipToNextLine();
+ } else if (st.sval.equals("l")) {
+ st.skipToNextLine();
+ } else if (st.sval.equals("mtllib")) {
+ loadMaterialFile(st);
+ } else if (st.sval.equals("usemtl")) {
+ readMaterialName(st);
+ } else if (st.sval.equals("maplib")) {
+ st.skipToNextLine();
+ } else if (st.sval.equals("usemap")) {
+ st.skipToNextLine();
+ } else {
+ throw new ParsingErrorException(
+ "Unrecognized token, line " + st.lineno());
+ }
+ }
+
+ st.skipToNextLine();
+
+ // Get next token
+ st.getToken();
+ }
+ } // End of readFile
+
+
+ /**
+ * Constructor.
+ *
+ * @param flags The constants from above or from
+ * com.sun.j3d.loaders.Loader, possibly "or'ed" (|) together.
+ * @param radians Ignored if the vn token is present in the model (user
+ * normals supplied). Otherwise, crease angle to use within smoothing
+ * groups, or within geometry groups if the s token isn't present either.
+ */
+ public ObjectFile(int flags, float radians) {
+ setFlags(flags);
+ this.radians = radians;
+ } // End of ObjectFile(int, float)
+
+
+ /**
+ * Constructor. Crease Angle set to default of
+ * 44 degrees (see NormalGenerator utility for details).
+ * @param flags The constants from above or from
+ * com.sun.j3d.loaders.Loader, possibly "or'ed" (|) together.
+ */
+ public ObjectFile(int flags) {
+ this(flags, -1.0f);
+ } // End of ObjectFile(int)
+
+
+ /**
+ * Default constructor. Crease Angle set to default of
+ * 44 degrees (see NormalGenerator utility for details). Flags
+ * set to zero (0).
+ */
+ public ObjectFile() {
+ this(0, -1.0f);
+ } // End of ObjectFile()
+
+
+ /**
+ * 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)));
+ }
+ } // End of setBasePathFromFilename
+
+
+ /**
+ * The Object File is loaded from the .obj file specified by
+ * the filename.
+ * To attach the model to your scene, call getSceneGroup() on
+ * the Scene object passed back, and attach the returned
+ * BranchGroup to your scene graph. For an example, see
+ * j3d-examples/ObjLoad/ObjLoad.java.
+ */
+ public Scene load(String filename) throws FileNotFoundException,
+ IncorrectFormatException,
+ ParsingErrorException {
+
+ setBasePathFromFilename(filename);
+
+ Reader reader = new BufferedReader(new FileReader(filename));
+ return load(reader);
+ } // End of load(String)
+
+
+ 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 {
+ baseUrl = new URL(s);
+ }
+ catch (MalformedURLException e) {
+ throw new FileNotFoundException(e.getMessage());
+ }
+ } // End of setBaseUrlFromUrl
+
+
+ /**
+ * The object file is loaded off of the web.
+ * To attach the model to your scene, call getSceneGroup() on
+ * the Scene object passed back, and attach the returned
+ * BranchGroup to your scene graph. For an example, see
+ * j3d-examples/ObjLoad/ObjLoad.java.
+ */
+ public Scene load(URL url) throws FileNotFoundException,
+ IncorrectFormatException,
+ ParsingErrorException {
+ BufferedReader reader;
+
+ if (baseUrl == null) setBaseUrlFromUrl(url);
+
+ try {
+ reader = new BufferedReader(new InputStreamReader(url.openStream()));
+ }
+ catch (IOException e) {
+ throw new FileNotFoundException(e.getMessage());
+ }
+ fromUrl = true;
+ return load(reader);
+ } // End of load(URL)
+
+
+ /**
+ * getLimits
+ *
+ * Returns an array of Point3f which form a bounding box around the
+ * object. Element 0 is the low value, element 1 is the high value.
+ * See normalize() below for an example of how to use this method.
+ */
+ private Point3f[] getLimits() {
+ Point3f cur_vtx = new Point3f();
+
+ // Find the limits of the model
+ Point3f[] limit = new Point3f[2];
+ limit[0] = new Point3f(Float.MAX_VALUE, Float.MAX_VALUE, Float.MAX_VALUE);
+ limit[1] = new Point3f(Float.MIN_VALUE, Float.MIN_VALUE, Float.MIN_VALUE);
+ for (int i = 0 ; i < coordList.size() ; i++) {
+
+ cur_vtx = (Point3f)coordList.get(i);
+
+ // Keep track of limits for normalization
+ if (cur_vtx.x < limit[0].x) limit[0].x = cur_vtx.x;
+ if (cur_vtx.x > limit[1].x) limit[1].x = cur_vtx.x;
+ if (cur_vtx.y < limit[0].y) limit[0].y = cur_vtx.y;
+ if (cur_vtx.y > limit[1].y) limit[1].y = cur_vtx.y;
+ if (cur_vtx.z < limit[0].z) limit[0].z = cur_vtx.z;
+ if (cur_vtx.z > limit[1].z) limit[1].z = cur_vtx.z;
+ }
+
+ if ((DEBUG & 64) != 0) {
+ System.out.println("Model range: (" +
+ limit[0].x + "," + limit[0].y + "," + limit[0].z + ") to (" +
+ limit[1].x + "," + limit[1].y + "," + limit[1].z + ")");
+ }
+
+ return limit;
+ } // End of getLimits
+
+
+
+ /**
+ * Center the object and make it (-1,-1,-1) to (1,1,1).
+ */
+ private void resize() {
+ int i, j;
+ Point3f cur_vtx = new Point3f();
+ float biggest_dif;
+
+ Point3f[] limit = getLimits();
+
+ // Move object so it's centered on (0,0,0)
+ Vector3f offset = new Vector3f(-0.5f * (limit[0].x + limit[1].x),
+ -0.5f * (limit[0].y + limit[1].y),
+ -0.5f * (limit[0].z + limit[1].z));
+
+ if ((DEBUG & 64) != 0) {
+ System.out.println("Offset amount: (" +
+ offset.x + "," + offset.y + "," + offset.z + ")");
+ }
+
+ // Find the divide-by value for the normalization
+ biggest_dif = limit[1].x - limit[0].x;
+ if (biggest_dif < limit[1].y - limit[0].y)
+ biggest_dif = limit[1].y - limit[0].y;
+ if (biggest_dif < limit[1].z - limit[0].z)
+ biggest_dif = limit[1].z - limit[0].z;
+ biggest_dif /= 2.0f;
+
+ for (i = 0 ; i < coordList.size() ; i++) {
+
+ cur_vtx = (Point3f)coordList.get(i);
+
+ cur_vtx.add(cur_vtx, offset);
+
+ cur_vtx.x /= biggest_dif;
+ cur_vtx.y /= biggest_dif;
+ cur_vtx.z /= biggest_dif;
+
+ // coordList.setElementAt(cur_vtx, i);
+ }
+ } // End of resize
+
+
+ private int[] objectToIntArray(ArrayList inList) {
+ int outList[] = new int[inList.size()];
+ for (int i = 0 ; i < inList.size() ; i++) {
+ outList[i] = ((Integer)inList.get(i)).intValue();
+ }
+ return outList;
+ } // End of objectToIntArray
+
+
+ private Point3f[] objectToPoint3Array(ArrayList inList) {
+ Point3f outList[] = new Point3f[inList.size()];
+ for (int i = 0 ; i < inList.size() ; i++) {
+ outList[i] = (Point3f)inList.get(i);
+ }
+ return outList;
+ } // End of objectToPoint3Array
+
+
+
+ private TexCoord2f[] objectToTexCoord2Array(ArrayList inList) {
+ TexCoord2f outList[] = new TexCoord2f[inList.size()];
+ for (int i = 0 ; i < inList.size() ; i++) {
+ outList[i] = (TexCoord2f)inList.get(i);
+ }
+ return outList;
+ } // End of objectToTexCoord2Array
+
+
+ private Vector3f[] objectToVectorArray(ArrayList inList) {
+ Vector3f outList[] = new Vector3f[inList.size()];
+ for (int i = 0 ; i < inList.size() ; i++) {
+ outList[i] = (Vector3f)inList.get(i);
+ }
+ return outList;
+ } // End of objectToVectorArray
+
+
+ /**
+ * Each group is a list of indices into the model's index lists,
+ * indicating the starting index of each triangle in the group.
+ * This method converts those data structures
+ * into an integer array to use with GeometryInfo.
+ */
+ private int[] groupIndices(ArrayList sourceList, ArrayList group) {
+ int indices[] = new int[group.size() * 3];
+ for (int i = 0 ; i < group.size() ; i++) {
+ int j = ((Integer)group.get(i)).intValue();
+ indices[i * 3 + 0] = ((Integer)sourceList.get(j + 0)).intValue();
+ indices[i * 3 + 1] = ((Integer)sourceList.get(j + 1)).intValue();
+ indices[i * 3 + 2] = ((Integer)sourceList.get(j + 2)).intValue();
+ }
+ return indices;
+ } // end of groupIndices
+
+
+ /**
+ * smoothingGroupNormals
+ *
+ * Smoothing groups are groups of faces who should be grouped
+ * together for normal calculation purposes. The faces are
+ * put into a GeometryInfo object and normals are calculated
+ * with a 180 degree creaseAngle (no creases) or whatever the
+ * user has specified. The normals
+ * are then copied out of the GeometryInfo and back into
+ * ObjectFile data structures.
+ */
+ private void smoothingGroupNormals() {
+ NormalGenerator ng =
+ new NormalGenerator(radians == -1.0f ? Math.PI : radians);
+ NormalGenerator ng0 = new NormalGenerator(0.0);
+ normList.clear();
+ normIdxList = null;
+ int newNormIdxArray[] = new int[coordIdxList.size()];
+
+ Iterator e = triSgroups.keySet().iterator();
+ while (e.hasNext()) {
+ String curname = (String)e.next();
+ ArrayList triList = (ArrayList)triSgroups.get(curname);
+
+ // Check for group with no faces
+ if (triList.size() > 0) {
+
+ GeometryInfo gi = new GeometryInfo(GeometryInfo.TRIANGLE_ARRAY);
+
+ gi.setCoordinateIndices(groupIndices(coordIdxList, triList));
+ gi.setCoordinates(coordArray);
+
+ if (curname.equals("0")) ng0.generateNormals(gi);
+ else ng.generateNormals(gi);
+
+ // Get the generated normals and indices
+ Vector3f genNorms[] = gi.getNormals();
+ int genNormIndices[] = gi.getNormalIndices();
+
+ // Now we need to copy the generated normals into ObjectFile
+ // data structures (normList and normIdxList). The variable
+ // normIdx is the index of the index of the normal currently
+ // being put into the list. It takes some calculation to
+ // figure out the new index and where to put it.
+ int normIdx = 0;
+ // Repeat for each triangle in the smoothing group
+ for (int i = 0 ; i < triList.size() ; i++) {
+
+ // Get the coordIdxList index of the first index in this face
+ int idx = ((Integer)triList.get(i)).intValue();
+
+ // Repeat for each vertex in the triangle
+ for (int j = 0 ; j < 3 ; j++) {
+
+ // Put the new normal's index into the index list
+ newNormIdxArray[idx + j] = normList.size();
+
+ // Add the vertex's normal to the normal list
+ normList.add(genNorms[genNormIndices[normIdx++]]);
+ }
+ }
+ }
+ }
+ normIdxList = new ArrayList(coordIdxList.size());
+ for (int i = 0 ; i < coordIdxList.size() ; i++) {
+ normIdxList.add(new Integer(newNormIdxArray[i]));
+ }
+ normArray = objectToVectorArray(normList);
+ } // end of smoothingGroupNormals
+
+
+ /**
+ * Each face is converted to triangles. As each face is converted,
+ * we look up which geometry group and smoothing group the face
+ * belongs to. The generated triangles are added to each of these
+ * groups, which are also being converted to a new triangle based format.
+ *
+ * We need to convert to triangles before normals are generated
+ * because of smoothing groups. The faces in a smoothing group
+ * are copied into a GeometryInfo to have their normals calculated,
+ * and then the normals are copied out of the GeometryInfo using
+ * GeometryInfo.getNormalIndices. As part of Normal generation,
+ * the geometry gets converted to Triangles. So we need to convert
+ * to triangles *before* Normal generation so that the normals we
+ * read out of the GeometryInfo match up with the vertex data
+ * that we sent in. If we sent in TRIANGLE_FAN data, the normal
+ * generator would convert it to triangles and we'd read out
+ * normals formatted for Triangle data. This would not match up
+ * with our original Fan data, so we couldn't tell which normals
+ * go with which vertices.
+ */
+ private void convertToTriangles() {
+ boolean triangulate = (flags & TRIANGULATE) != 0;
+ boolean textures = !texList.isEmpty() && !texIdxList.isEmpty() &&
+ (texIdxList.size() == coordIdxList.size());
+ boolean normals = !normList.isEmpty() && !normIdxList.isEmpty() &&
+ (normIdxList.size() == coordIdxList.size());
+ int numFaces = stripCounts.size();
+ boolean haveSgroups = curSgroup != null;
+
+ triGroups = new HashMap(50);
+ if (haveSgroups) triSgroups = new HashMap(50);
+
+ ArrayList newCoordIdxList = null;
+ ArrayList newTexIdxList = null;
+ ArrayList newNormIdxList = null;
+
+ if (triangulate) {
+ GeometryInfo gi = new GeometryInfo(GeometryInfo.POLYGON_ARRAY);
+ gi.setStripCounts(objectToIntArray(stripCounts));
+ gi.setCoordinates(coordArray);
+ gi.setCoordinateIndices(objectToIntArray(coordIdxList));
+ if (textures) {
+ gi.setTextureCoordinateParams(1, 2);
+ gi.setTextureCoordinates(0, texArray);
+ gi.setTextureCoordinateIndices(0, objectToIntArray(texIdxList));
+ }
+ if (normals) {
+ gi.setNormals(normArray);
+ gi.setNormalIndices(objectToIntArray(normIdxList));
+ }
+ gi.convertToIndexedTriangles();
+
+ // Data is now indexed triangles. Next step is to take the data
+ // out of the GeometryInfo and put into internal data structures
+
+ int coordIndicesArray[] = gi.getCoordinateIndices();
+
+ // Fix for #4366060
+ // Make sure triangulated geometry has the correct number of triangles
+ int tris = 0;
+ for (int i = 0 ; i < numFaces ; i++)
+ tris += ((Integer)stripCounts.get(i)).intValue() - 2;
+
+ if (coordIndicesArray.length != (tris * 3)) {
+ // Model contains bad polygons that didn't triangulate into the
+ // correct number of triangles. Fall back to "simple" triangulation
+ triangulate = false;
+ } else {
+
+ int texIndicesArray[] = gi.getTextureCoordinateIndices();
+ int normIndicesArray[] = gi.getNormalIndices();
+
+ // Convert index arrays to internal ArrayList format
+ coordIdxList.clear();
+ texIdxList.clear();
+ normIdxList.clear();
+ for (int i = 0 ; i < coordIndicesArray.length ; i++) {
+ coordIdxList.add(new Integer(coordIndicesArray[i]));
+ if (textures) texIdxList.add(new Integer(texIndicesArray[i]));
+ if (normals) normIdxList.add(new Integer(normIndicesArray[i]));
+ }
+ }
+ }
+
+ if (!triangulate) {
+ newCoordIdxList = new ArrayList();
+ if (textures) newTexIdxList = new ArrayList();
+ if (normals) newNormIdxList = new ArrayList();
+ }
+
+ // Repeat for each face in the model - add the triangles from each
+ // face to the Geometry and Smoothing Groups
+ int baseVertex = 0;
+ for (int f = 0 ; f < numFaces ; f++) {
+ int faceSize = ((Integer)stripCounts.get(f)).intValue();
+
+ // Find out the name of the group to which this face belongs
+ Integer curFace = new Integer(f);
+ curGroup = (String)groups.get(curFace);
+
+ // Change to a new geometry group, create if it doesn't exist
+ curTriGroup = (ArrayList)triGroups.get(curGroup);
+ if (curTriGroup == null) {
+ curTriGroup = new ArrayList();
+ triGroups.put(curGroup, curTriGroup);
+ }
+
+ // Change to a new smoothing group, create if it doesn't exist
+ if (haveSgroups) {
+ curSgroup = (String)sGroups.get(curFace);
+ if (curSgroup == null) {
+ // Weird case - this face has no smoothing group. Happens if the
+ // first 's' token comes after some faces have already been defined.
+ // Assume they wanted no smoothing for these faces
+ curSgroup = "0";
+ }
+ curTriSgroup = (ArrayList)triSgroups.get(curSgroup);
+ if (curTriSgroup == null) {
+ curTriSgroup = new ArrayList();
+ triSgroups.put(curSgroup, curTriSgroup);
+ }
+ }
+
+ if (triangulate) {
+
+ // Each polygon of n vertices is now n-2 triangles
+ for (int t = 0 ; t < faceSize - 2 ; t++) {
+
+ // The groups just remember the first vertex of each triangle
+ Integer triBaseVertex = new Integer(baseVertex);
+ curTriGroup.add(triBaseVertex);
+ if (haveSgroups) curTriSgroup.add(triBaseVertex);
+
+ baseVertex += 3;
+ }
+ } else {
+ // Triangulate simply
+ for (int v = 0 ; v < faceSize - 2 ; v++) {
+ // Add this triangle to the geometry group and the smoothing group
+ Integer triBaseVertex = new Integer(newCoordIdxList.size());
+ curTriGroup.add(triBaseVertex);
+ if (haveSgroups) curTriSgroup.add(triBaseVertex);
+
+ newCoordIdxList.add(coordIdxList.get(baseVertex));
+ newCoordIdxList.add(coordIdxList.get(baseVertex + v + 1));
+ newCoordIdxList.add(coordIdxList.get(baseVertex + v + 2));
+
+ if (textures) {
+ newTexIdxList.add(texIdxList.get(baseVertex));
+ newTexIdxList.add(texIdxList.get(baseVertex + v + 1));
+ newTexIdxList.add(texIdxList.get(baseVertex + v + 2));
+ }
+
+ if (normals) {
+ newNormIdxList.add(normIdxList.get(baseVertex));
+ newNormIdxList.add(normIdxList.get(baseVertex + v + 1));
+ newNormIdxList.add(normIdxList.get(baseVertex + v + 2));
+ }
+ }
+ baseVertex += faceSize;
+ }
+ }
+
+ // No need to keep these around
+ stripCounts = null;
+ groups = null;
+ sGroups = null;
+
+ if (!triangulate) {
+ coordIdxList = newCoordIdxList;
+ texIdxList = newTexIdxList;
+ normIdxList = newNormIdxList;
+ }
+ } // End of convertToTriangles
+
+
+ private SceneBase makeScene() {
+ // Create Scene to pass back
+ SceneBase scene = new SceneBase();
+ BranchGroup group = new BranchGroup();
+ scene.setSceneGroup(group);
+
+ boolean gen_norms = normList.isEmpty() || normIdxList.isEmpty() ||
+ (normIdxList.size() != coordIdxList.size());
+ boolean do_tex = !texList.isEmpty() && !texIdxList.isEmpty() &&
+ (texIdxList.size() == coordIdxList.size());
+
+ // Convert ArrayLists to arrays
+ coordArray = objectToPoint3Array(coordList);
+ if (!gen_norms) normArray = objectToVectorArray(normList);
+ if (do_tex) texArray = objectToTexCoord2Array(texList);
+
+ convertToTriangles();
+
+ if ((DEBUG & 8) != 0) {
+ time = System.currentTimeMillis() - time;
+ System.out.println("Convert to triangles: " + time + " ms");
+ time = System.currentTimeMillis();
+ }
+
+ if ((gen_norms) && (curSgroup != null)) {
+ smoothingGroupNormals();
+ gen_norms = false;
+ if ((DEBUG & 8) != 0) {
+ time = System.currentTimeMillis() - time;
+ System.out.println("Smoothing group normals: " + time + " ms");
+ time = System.currentTimeMillis();
+ }
+ }
+
+ NormalGenerator ng = null;
+ if (gen_norms) ng = new NormalGenerator(radians);
+
+ Stripifier strippy = null;
+ if ((flags & STRIPIFY) != 0) strippy = new Stripifier();
+
+ long t1 = 0, t2 = 0, t3 = 0, t4 = 0;
+
+ // Each "Group" of faces in the model will be one Shape3D
+ Iterator e = triGroups.keySet().iterator();
+ while (e.hasNext()) {
+
+ String curname = (String)e.next();
+ ArrayList triList = (ArrayList)triGroups.get(curname);
+
+ // Check for group with no faces
+ if (triList.size() > 0) {
+
+ GeometryInfo gi = new GeometryInfo(GeometryInfo.TRIANGLE_ARRAY);
+
+ gi.setCoordinateIndices(groupIndices(coordIdxList, triList));
+ gi.setCoordinates(coordArray);
+
+ if (do_tex) {
+ gi.setTextureCoordinateParams(1, 2);
+ gi.setTextureCoordinates(0, texArray);
+ gi.setTextureCoordinateIndices(0, groupIndices(texIdxList, triList));
+ }
+
+ if ((DEBUG & 8) != 0) time = System.currentTimeMillis();
+ if (gen_norms) {
+ if ((flags & REVERSE) != 0) gi.reverse();
+ ng.generateNormals(gi);
+ if ((DEBUG & 8) != 0) {
+ t2 += System.currentTimeMillis() - time;
+ System.out.println("Generate normals: " + t2 + " ms");
+ time = System.currentTimeMillis();
+ }
+ } else {
+ gi.setNormalIndices(groupIndices(normIdxList, triList));
+ gi.setNormals(normArray);
+ if ((flags & REVERSE) != 0) gi.reverse();
+ }
+
+ if ((flags & STRIPIFY) != 0) {
+ strippy.stripify(gi);
+ if ((DEBUG & 8) != 0) {
+ t3 += System.currentTimeMillis() - time;
+ System.out.println("Stripify: " + t3 + " ms");
+ time = System.currentTimeMillis();
+ }
+ }
+
+ // Put geometry into Shape3d
+ Shape3D shape = new Shape3D();
+
+ shape.setGeometry(gi.getGeometryArray(true, true, false));
+
+ String matName = (String)groupMaterials.get(curname);
+ materials.assignMaterial(matName, shape);
+
+ group.addChild(shape);
+ scene.addNamedObject(curname, shape);
+
+ if ((DEBUG & 8) != 0) {
+ t4 += System.currentTimeMillis() - time;
+ System.out.println("Shape 3D: " + t4 + " ms");
+ time = System.currentTimeMillis();
+ }
+ }
+ }
+
+ return scene;
+ } // end of makeScene
+
+
+ /**
+ * The Object File is loaded from the already opened file.
+ * To attach the model to your scene, call getSceneGroup() on
+ * the Scene object passed back, and attach the returned
+ * BranchGroup to your scene graph. For an example, see
+ * j3d-examples/ObjLoad/ObjLoad.java.
+ */
+ public Scene load(Reader reader) throws FileNotFoundException,
+ IncorrectFormatException,
+ ParsingErrorException {
+ // ObjectFileParser does lexical analysis
+ ObjectFileParser st = new ObjectFileParser(reader);
+
+ coordList = new ArrayList();
+ texList = new ArrayList();
+ normList = new ArrayList();
+ coordIdxList = new ArrayList();
+ texIdxList = new ArrayList();
+ normIdxList = new ArrayList();
+ groups = new HashMap(50);
+ curGroup = "default";
+ sGroups = new HashMap(50);
+ curSgroup = null;
+ stripCounts = new ArrayList();
+ groupMaterials = new HashMap(50);
+ groupMaterials.put(curGroup, "default");
+ materials = new ObjectFileMaterials();
+
+ time = 0L;
+ if ((DEBUG & 8) != 0) {
+ time = System.currentTimeMillis();
+ }
+
+ readFile(st);
+
+ if ((DEBUG & 8) != 0) {
+ time = System.currentTimeMillis() - time;
+ System.out.println("Read file: " + time + " ms");
+ time = System.currentTimeMillis();
+ }
+
+ if ((flags & RESIZE) != 0) resize();
+
+ return makeScene();
+ } // End of load(Reader)
+
+
+ /**
+ * For an .obj file loaded from a URL, set the URL where associated files
+ * (like material properties files) will be found.
+ * Only needs to be called to set it to a different URL
+ * from that containing the .obj file.
+ */
+ public void setBaseUrl(URL url) {
+ baseUrl = url;
+ } // End of setBaseUrl
+
+
+ /**
+ * Return the URL where files associated with this .obj file (like
+ * material properties files) will be found.
+ */
+ public URL getBaseUrl() {
+ return baseUrl;
+ } // End of getBaseUrl
+
+
+ /**
+ * 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) {
+ 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;
+ } // End of setBasePath
+
+
+ /**
+ * Return the path where files associated with this .obj file (like material
+ * files) are located.
+ */
+ public String getBasePath() {
+ return basePath;
+ } // End of getBasePath
+
+
+ /**
+ * Set parameters for loading the model.
+ * Flags defined in Loader.java are ignored by the ObjectFile Loader
+ * because the .obj file format doesn't include lights, fog, background,
+ * behaviors, views, or sounds. However, several flags are defined
+ * specifically for use with the ObjectFile Loader (see above).
+ */
+ public void setFlags(int flags) {
+ this.flags = flags;
+ if ((DEBUG & 4) != 0) System.out.println("Flags = " + flags);
+ } // End of setFlags
+
+
+ /**
+ * Get the parameters currently defined for loading the model.
+ * Flags defined in Loader.java are ignored by the ObjectFile Loader
+ * because the .obj file format doesn't include lights, fog, background,
+ * behaviors, views, or sounds. However, several flags are defined
+ * specifically for use with the ObjectFile Loader (see above).
+ */
+ public int getFlags() {
+ return flags;
+ } // End of getFlags
+
+} // End of class ObjectFile
+
+// End of file ObjectFile.java
diff --git a/src/classes/share/com/sun/j3d/loaders/objectfile/ObjectFileMaterials.java b/src/classes/share/com/sun/j3d/loaders/objectfile/ObjectFileMaterials.java
new file mode 100644
index 0000000..8dd3b2c
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/objectfile/ObjectFileMaterials.java
@@ -0,0 +1,425 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.objectfile;
+
+import javax.media.j3d.Appearance;
+import javax.media.j3d.Material;
+import javax.media.j3d.Shape3D;
+import javax.vecmath.Color3f;
+import com.sun.j3d.loaders.ParsingErrorException;
+import com.sun.j3d.loaders.IncorrectFormatException;
+import java.io.FileNotFoundException;
+import java.io.StringReader;
+import java.io.Reader;
+import java.io.FileReader;
+import java.io.BufferedReader;
+import java.io.BufferedInputStream;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.io.FileInputStream;
+import java.util.HashMap;
+import com.sun.j3d.loaders.objectfile.DefaultMaterials;
+import java.net.URL;
+import java.net.MalformedURLException;
+import java.awt.Toolkit;
+import java.awt.Image;
+import java.awt.image.BufferedImage;
+import javax.media.j3d.Texture2D;
+import java.awt.image.ImageObserver;
+import java.awt.image.PixelGrabber;
+import java.awt.image.DataBufferInt;
+import javax.media.j3d.ImageComponent2D;
+import javax.media.j3d.TexCoordGeneration;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import javax.media.j3d.GeometryArray;
+import com.sun.j3d.utils.image.TextureLoader;
+import javax.media.j3d.TransparencyAttributes;
+
+
+class ObjectFileMaterials implements ImageObserver {
+ // DEBUG
+ // 1 = Name of materials
+ // 16 = Tokens
+ private static final int DEBUG = 0;
+
+ private String curName = null;
+ private ObjectFileMaterial cur = null;
+
+ private HashMap materials; // key=String name of material
+ // value=ObjectFileMaterial
+
+ private String basePath;
+ private boolean fromUrl;
+
+
+ private class ObjectFileMaterial {
+
+ public Color3f Ka;
+ public Color3f Kd;
+ public Color3f Ks;
+ public int illum;
+ public float Ns;
+ public Texture2D t;
+ public boolean transparent;
+
+ public void ObjectFileMaterial() {
+ Ka = null;
+ Kd = null;
+ Ks = null;
+ illum = -1;
+ Ns = -1.0f;
+ t = null;
+ } // End of ObjectFileMaterial
+ }
+
+
+ void assignMaterial(String matName, Shape3D shape) {
+ ObjectFileMaterial p = null;
+
+ if ((DEBUG & 1) != 0) System.out.println("Color " + matName);
+
+ Material m = new Material();
+ p = (ObjectFileMaterial)materials.get(matName);
+ Appearance a = new Appearance();
+
+ if (p != null) {
+ // Set ambient & diffuse color
+ if (p.Ka != null) m.setAmbientColor(p.Ka);
+ if (p.Kd != null) m.setDiffuseColor(p.Kd);
+
+ // Set specular color
+ if ((p.Ks != null) && (p.illum != 1)) m.setSpecularColor(p.Ks);
+ else if (p.illum == 1) m.setSpecularColor(0.0f, 0.0f, 0.0f);
+
+ if (p.illum >= 1) m.setLightingEnable(true);
+ else if (p.illum == 0) m.setLightingEnable(false);
+
+ if (p.Ns != -1.0f) m.setShininess(p.Ns);
+
+ if (p.t != null) {
+ a.setTexture(p.t);
+ // Create Texture Coordinates if not already present
+ if ((((GeometryArray)shape.getGeometry()).getVertexFormat() &
+ GeometryArray.TEXTURE_COORDINATE_2) == 0) {
+ TexCoordGeneration tcg = new TexCoordGeneration();
+ a.setTexCoordGeneration(tcg);
+ }
+ }
+
+ if (p.transparent)
+ a.setTransparencyAttributes(
+ new TransparencyAttributes(TransparencyAttributes.NICEST,
+ 0.0f));
+ }
+ a.setMaterial(m);
+ if ((DEBUG & 1) != 0) System.out.println(m);
+ shape.setAppearance(a);
+ } // End of assignMaterial
+
+
+ private void readName(ObjectFileParser st) throws ParsingErrorException {
+ st.getToken();
+
+ if (st.ttype == ObjectFileParser.TT_WORD) {
+
+ if (curName != null) materials.put(curName, cur);
+ curName = new String(st.sval);
+ cur = new ObjectFileMaterial();
+ }
+ st.skipToNextLine();
+ } // End of readName
+
+
+ private void readAmbient(ObjectFileParser st) throws ParsingErrorException {
+ Color3f p = new Color3f();
+
+ st.getNumber();
+ p.x = (float)st.nval;
+ st.getNumber();
+ p.y = (float)st.nval;
+ st.getNumber();
+ p.z = (float)st.nval;
+
+ cur.Ka = p;
+
+ st.skipToNextLine();
+ } // End of readAmbient
+
+
+ private void readDiffuse(ObjectFileParser st) throws ParsingErrorException {
+ Color3f p = new Color3f();
+
+ st.getNumber();
+ p.x = (float)st.nval;
+ st.getNumber();
+ p.y = (float)st.nval;
+ st.getNumber();
+ p.z = (float)st.nval;
+
+ cur.Kd = p;
+
+ st.skipToNextLine();
+ } // End of readDiffuse
+
+
+ private void readSpecular(ObjectFileParser st) throws ParsingErrorException {
+ Color3f p = new Color3f();
+
+ st.getNumber();
+ p.x = (float)st.nval;
+ st.getNumber();
+ p.y = (float)st.nval;
+ st.getNumber();
+ p.z = (float)st.nval;
+
+ cur.Ks = p;
+
+ st.skipToNextLine();
+ } // End of readSpecular
+
+
+ private void readIllum(ObjectFileParser st) throws ParsingErrorException {
+ float f;
+
+ st.getNumber();
+ cur.illum = (int)st.nval;
+
+ st.skipToNextLine();
+ } // End of readSpecular
+
+
+ private void readShininess(ObjectFileParser st) throws ParsingErrorException {
+ float f;
+
+ st.getNumber();
+ cur.Ns = (float)st.nval;
+ if (cur.Ns < 1.0f) cur.Ns = 1.0f;
+ else if (cur.Ns > 128.0f) cur.Ns = 128.0f;
+
+ st.skipToNextLine();
+ } // End of readSpecular
+
+
+ public void readMapKd(ObjectFileParser st) {
+ // Filenames are case sensitive
+ st.lowerCaseMode(false);
+
+ // Get name of texture file (skip path)
+ String tFile = null;
+ do {
+ st.getToken();
+ if (st.ttype == ObjectFileParser.TT_WORD) tFile = st.sval;
+ } while (st.ttype != ObjectFileParser.TT_EOL);
+
+ st.lowerCaseMode(true);
+
+ if (tFile != null) {
+ // Check for filename with no extension
+ if (tFile.lastIndexOf('.') != -1) {
+ try {
+ // Convert filename to lower case for extension comparisons
+ String suffix =
+ tFile.substring(tFile.lastIndexOf('.') + 1).toLowerCase();
+
+ TextureLoader t = null;
+
+ if ((suffix.equals("int")) || (suffix.equals("inta")) ||
+ (suffix.equals("rgb")) || (suffix.equals("rgba")) ||
+ (suffix.equals("bw")) || (suffix.equals("sgi"))) {
+ RgbFile f;
+ if (fromUrl) {
+ f = new RgbFile(new URL(basePath + tFile).openStream());
+ } else {
+ f = new RgbFile(new FileInputStream(basePath + tFile));
+ }
+ BufferedImage bi = f.getImage();
+
+ boolean luminance = suffix.equals("int") || suffix.equals("inta");
+ boolean alpha = suffix.equals("inta") || suffix.equals("rgba");
+ cur.transparent = alpha;
+
+ String s = null;
+ if (luminance && alpha) s = "LUM8_ALPHA8";
+ else if (luminance) s = "LUMINANCE";
+ else if (alpha) s = "RGBA";
+ else s = "RGB";
+
+ t = new TextureLoader(bi, s, TextureLoader.GENERATE_MIPMAP);
+ } else {
+ // For all other file types, use the TextureLoader
+ if (fromUrl) {
+ t = new TextureLoader(new URL(basePath + tFile), "RGB",
+ TextureLoader.GENERATE_MIPMAP, null);
+ } else {
+ t = new TextureLoader(basePath + tFile, "RGB",
+ TextureLoader.GENERATE_MIPMAP, null);
+ }
+ }
+ Texture2D texture = (Texture2D)t.getTexture();
+ if (texture != null) cur.t = texture;
+ }
+ catch (FileNotFoundException e) {
+ // Texture won't get loaded if file can't be found
+ }
+ catch (MalformedURLException e) {
+ // Texture won't get loaded if file can't be found
+ }
+ catch (IOException e) {
+ // Texture won't get loaded if file can't be found
+ }
+ }
+ }
+ st.skipToNextLine();
+ } // End of readMapKd
+
+
+ private void readFile(ObjectFileParser st) throws ParsingErrorException {
+ int t;
+ st.getToken();
+ while (st.ttype != ObjectFileParser.TT_EOF) {
+
+ // Print out one token for each line
+ if ((DEBUG & 16) != 0) {
+ System.out.print("Token ");
+ if (st.ttype == ObjectFileParser.TT_EOL) System.out.println("EOL");
+ else if (st.ttype == ObjectFileParser.TT_WORD)
+ System.out.println(st.sval);
+ else System.out.println((char)st.ttype);
+ }
+
+ if (st.ttype == ObjectFileParser.TT_WORD) {
+ if (st.sval.equals("newmtl")) {
+ readName(st);
+ } else if (st.sval.equals("ka")) {
+ readAmbient(st);
+ } else if (st.sval.equals("kd")) {
+ readDiffuse(st);
+ } else if (st.sval.equals("ks")) {
+ readSpecular(st);
+ } else if (st.sval.equals("illum")) {
+ readIllum(st);
+ } else if (st.sval.equals("d")) {
+ st.skipToNextLine();
+ } else if (st.sval.equals("ns")) {
+ readShininess(st);
+ } else if (st.sval.equals("tf")) {
+ st.skipToNextLine();
+ } else if (st.sval.equals("sharpness")) {
+ st.skipToNextLine();
+ } else if (st.sval.equals("map_kd")) {
+ readMapKd(st);
+ } else if (st.sval.equals("map_ka")) {
+ st.skipToNextLine();
+ } else if (st.sval.equals("map_ks")) {
+ st.skipToNextLine();
+ } else if (st.sval.equals("map_ns")) {
+ st.skipToNextLine();
+ } else if (st.sval.equals("bump")) {
+ st.skipToNextLine();
+ }
+ }
+
+ st.skipToNextLine();
+
+ // Get next token
+ st.getToken();
+ }
+ if (curName != null) materials.put(curName, cur);
+ } // End of readFile
+
+
+ void readMaterialFile(boolean fromUrl, String basePath, String fileName)
+ throws ParsingErrorException {
+
+ Reader reader;
+
+ this.basePath = basePath;
+ this.fromUrl = fromUrl;
+
+ try {
+ if (fromUrl) {
+ reader = (Reader)
+ (new InputStreamReader(
+ new BufferedInputStream(
+ (new URL(basePath + fileName).openStream()))));
+ } else {
+ reader = new BufferedReader(new FileReader(basePath + fileName));
+ }
+ }
+ catch (IOException e) {
+ // couldn't find it - ignore mtllib
+ return;
+ }
+ if ((DEBUG & 1) != 0)
+ System.out.println("Material file: " + basePath + fileName);
+
+ ObjectFileParser st = new ObjectFileParser(reader);
+ readFile(st);
+ } // End of readMaterialFile
+
+
+ ObjectFileMaterials() throws ParsingErrorException {
+ Reader reader = new StringReader(DefaultMaterials.materials);
+
+ ObjectFileParser st = new ObjectFileParser(reader);
+ materials = new HashMap(50);
+ readFile(st);
+ } // End of ObjectFileMaterials
+
+
+ /**
+ * Implement the ImageObserver interface. Needed to load jpeg and gif
+ * files using the Toolkit.
+ */
+ public boolean imageUpdate(Image img, int flags,
+ int x, int y, int w, int h) {
+
+ return (flags & (ALLBITS | ABORT)) == 0;
+ } // End of imageUpdate
+
+} // End of class ObjectFileMaterials
+
+// End of file ObjectFileMaterials.java
diff --git a/src/classes/share/com/sun/j3d/loaders/objectfile/ObjectFileParser.java b/src/classes/share/com/sun/j3d/loaders/objectfile/ObjectFileParser.java
new file mode 100644
index 0000000..ded314a
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/objectfile/ObjectFileParser.java
@@ -0,0 +1,179 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.objectfile;
+
+import java.io.StreamTokenizer;
+import java.io.IOException;
+import java.io.Reader;
+import com.sun.j3d.loaders.ParsingErrorException;
+
+class ObjectFileParser extends StreamTokenizer {
+
+ private static final char BACKSLASH = '\\';
+
+
+ /**
+ * setup
+ *
+ * Sets up StreamTokenizer for reading ViewPoint .obj file format.
+ */
+ void setup() {
+ resetSyntax();
+ eolIsSignificant(true);
+ lowerCaseMode(true);
+
+ // All printable ascii characters
+ wordChars('!', '~');
+
+ // Comment from ! to end of line
+ commentChar('!');
+
+ whitespaceChars(' ', ' ');
+ whitespaceChars('\n', '\n');
+ whitespaceChars('\r', '\r');
+ whitespaceChars('\t', '\t');
+
+ // These characters returned as tokens
+ ordinaryChar('#');
+ ordinaryChar('/');
+ ordinaryChar(BACKSLASH);
+ } // End of setup
+
+
+ /**
+ * getToken
+ *
+ * Gets the next token from the stream. Puts one of the four
+ * constants (TT_WORD, TT_NUMBER, TT_EOL, or TT_EOF) or the token value
+ * for single character tokens into ttype. Handles backslash
+ * continuation of lines.
+ */
+ void getToken() throws ParsingErrorException {
+ int t;
+ boolean done = false;
+
+ try {
+ do {
+ t = nextToken();
+ if (t == BACKSLASH) {
+ t = nextToken();
+ if (ttype != TT_EOL) done = true;
+ } else done = true;
+ } while (!done);
+ }
+ catch (IOException e) {
+ throw new ParsingErrorException(
+ "IO error on line " + lineno() + ": " + e.getMessage());
+ }
+ } // End of getToken
+
+
+ void printToken() {
+ switch (ttype) {
+ case TT_EOL:
+ System.out.println("Token EOL");
+ break;
+ case TT_EOF:
+ System.out.println("Token EOF");
+ break;
+ case TT_WORD:
+ System.out.println("Token TT_WORD: " + sval);
+ break;
+ case '/':
+ System.out.println("Token /");
+ break;
+ case BACKSLASH:
+ System.out.println("Token " + BACKSLASH);
+ break;
+ case '#':
+ System.out.println("Token #");
+ break;
+ }
+ } // end of printToken
+
+
+ /**
+ * skipToNextLine
+ *
+ * Skips all tokens on the rest of this line. Doesn't do anything if
+ * We're already at the end of a line
+ */
+ void skipToNextLine() throws ParsingErrorException {
+ while (ttype != TT_EOL) {
+ getToken();
+ }
+ } // end of skipToNextLine
+
+
+ /**
+ * getNumber
+ *
+ * Gets a number from the stream. Note that we don't recognize
+ * numbers in the tokenizer automatically because numbers might be in
+ * scientific notation, which isn't processed correctly by
+ * StreamTokenizer. The number is returned in nval.
+ */
+ void getNumber() throws ParsingErrorException {
+ int t;
+
+ try {
+ getToken();
+ if (ttype != TT_WORD)
+ throw new ParsingErrorException("Expected number on line " + lineno());
+ nval = (Double.valueOf(sval)).doubleValue();
+ }
+ catch (NumberFormatException e) {
+ throw new ParsingErrorException(e.getMessage());
+ }
+ } // end of getNumber
+
+
+ // ObjectFileParser constructor
+ ObjectFileParser(Reader r) {
+ super(r);
+ setup();
+ } // end of ObjectFileParser
+
+} // End of file ObjectFileParser.java
diff --git a/src/classes/share/com/sun/j3d/loaders/objectfile/RgbFile.java b/src/classes/share/com/sun/j3d/loaders/objectfile/RgbFile.java
new file mode 100644
index 0000000..5512ec2
--- /dev/null
+++ b/src/classes/share/com/sun/j3d/loaders/objectfile/RgbFile.java
@@ -0,0 +1,202 @@
+/*
+ * $RCSfile$
+ *
+ * Copyright (c) 2004 Sun Microsystems, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistribution of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistribution in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * Neither the name of Sun Microsystems, Inc. or the names of
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * This software is provided "AS IS," without a warranty of any
+ * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
+ * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
+ * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
+ * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
+ * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
+ * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
+ * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
+ * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
+ * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
+ * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGES.
+ *
+ * You acknowledge that this software is not designed, licensed or
+ * intended for use in the design, construction, operation or
+ * maintenance of any nuclear facility.
+ *
+ * $Revision$
+ * $Date$
+ * $State$
+ */
+
+package com.sun.j3d.loaders.objectfile;
+
+import java.io.BufferedInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.awt.image.DataBuffer;
+import java.awt.image.DataBufferByte;
+import java.awt.image.ComponentColorModel;
+import java.awt.image.WritableRaster;
+import java.awt.color.ColorSpace;
+import java.awt.Transparency;
+
+class RgbFile extends BufferedInputStream {
+
+ // Header data
+ short dimension;
+ short xSize;
+ short ySize;
+ short zSize;
+
+ String filename;
+
+ private static final int DEBUG = 0;
+
+
+ short getShort() throws IOException {
+ int t1 = (short)read();
+ if (t1 == -1) throw new IOException("Unexpected EOF");
+ int t2 = (short)read();
+ if (t2 == -1) throw new IOException("Unexpected EOF");
+ return (short)((t1 << 8) | t2);
+ } // End of getShort()
+
+
+ byte getByte() throws IOException {
+ int t = read();
+ if (t == -1) throw new IOException("Unexpected EOF");
+ return (byte)t;
+ } // End of getByte
+
+
+ int getInt() throws IOException {
+ int ret = 0;
+ for (int i = 0 ; i < 4 ; i++) {
+ int t = read();
+ if (t == -1) throw new IOException("Unexpected EOF");
+ ret = (ret << 8) | t;
+ }
+ return ret;
+ } // end of getInt
+
+
+ public BufferedImage getImage() throws IOException {
+ short magic = getShort();
+
+ if (magic != 474) throw new IOException("Unrecognized file format.");
+
+ byte storage = getByte();
+
+ if (storage != 0)
+ throw new IOException("RLE Compressed files not supported");
+
+ byte bpc = getByte();
+ dimension = getShort();
+ xSize = getShort();
+ ySize = getShort();
+ zSize = getShort();
+ int pixMin = getInt();
+ int pixMax = getInt();
+ skip(84l);
+ int colorMap = getInt();
+
+ if ((DEBUG & 1) != 0) {
+ System.out.println(filename + ":");
+ System.out.println(" bpc = " + bpc);
+ System.out.println(" dimension = " + dimension);
+ System.out.println(" xSize = " + xSize);
+ System.out.println(" ySize = " + ySize);
+ System.out.println(" zSize = " + zSize);
+ System.out.println(" pixMin = " + pixMin);
+ System.out.println(" pixMax = " + pixMax);
+ System.out.println(" colorMap = " + colorMap);
+ }
+
+ if ((pixMin != 0) || (pixMax != 0xff) || (colorMap != 0) || (bpc != 1))
+ throw new IOException("Unsupported options in file");
+
+ skip(404l);
+
+ ComponentColorModel cm = null;
+ if (zSize == 1) {
+ // Black and White image
+ ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
+
+ int[] nBits = {8};
+ cm = new ComponentColorModel(cs, nBits, false, false,
+ Transparency.OPAQUE,
+ DataBuffer.TYPE_BYTE);
+
+ } else if (zSize == 2) {
+ // Black and White image with alpha
+ ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
+
+ int[] nBits = {8, 8};
+ cm = new ComponentColorModel(cs, nBits, true, false,
+ Transparency.TRANSLUCENT,
+ DataBuffer.TYPE_BYTE);
+
+ } else if (zSize == 3) {
+ // RGB Image
+ ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
+
+ int[] nBits = {8, 8, 8};
+ cm = new ComponentColorModel(cs, nBits, false, false,
+ Transparency.OPAQUE,
+ DataBuffer.TYPE_BYTE);
+
+ } else if (zSize == 4) {
+ // RGBA Image
+ ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
+
+ int[] nBits = {8, 8, 8, 8};
+ cm = new ComponentColorModel(cs, nBits, true, false,
+ Transparency.TRANSLUCENT,
+ DataBuffer.TYPE_BYTE);
+ } else {
+ throw new IOException("Unsupported options in file");
+ }
+
+ WritableRaster r = cm.createCompatibleWritableRaster(xSize, ySize);
+ BufferedImage bi = new BufferedImage(cm, r, false, null);
+
+ int t;
+ byte image[] = ((DataBufferByte)r.getDataBuffer()).getData();
+ for (short z = 0 ; z < zSize ; z++) {
+ for (int y = ySize - 1 ; y >= 0 ; y--) {
+ for (short x = 0 ; x < xSize ; x++) {
+ t = read();
+ if (t == -1) throw new IOException("Unexpected EOF");
+ image[y * (xSize * zSize) + (x * zSize) + z] = (byte)t;
+ }
+ }
+ }
+
+ return bi;
+ } // End of getImage
+
+
+ public RgbFile(InputStream s) {
+ super(s);
+ } // End of RgbFile(URL)
+
+} // End of class RgbFile
+
+// End of file RgbFile.java