diff options
author | Kevin Rushforth <[email protected]> | 2004-06-09 04:13:21 +0000 |
---|---|---|
committer | Kevin Rushforth <[email protected]> | 2004-06-09 04:13:21 +0000 |
commit | 7fad83338964ecd32303d6e2737b19963492ecfe (patch) | |
tree | 886fd0b010966b11d6f25865715de4d5aa75cbb6 /src/classes/share/com/sun/j3d/loaders | |
parent | 4fc115fa7dfc9fe2d614a5e7d3a0db49431930cf (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')
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 |