0001: /*
0002: * $RCSfile: ObjectFile.java,v $
0003: *
0004: * Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * Redistribution and use in source and binary forms, with or without
0007: * modification, are permitted provided that the following conditions
0008: * are met:
0009: *
0010: * - Redistribution of source code must retain the above copyright
0011: * notice, this list of conditions and the following disclaimer.
0012: *
0013: * - Redistribution in binary form must reproduce the above copyright
0014: * notice, this list of conditions and the following disclaimer in
0015: * the documentation and/or other materials provided with the
0016: * distribution.
0017: *
0018: * Neither the name of Sun Microsystems, Inc. or the names of
0019: * contributors may be used to endorse or promote products derived
0020: * from this software without specific prior written permission.
0021: *
0022: * This software is provided "AS IS," without a warranty of any
0023: * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
0024: * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
0025: * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
0026: * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
0027: * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
0028: * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
0029: * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
0030: * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
0031: * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
0032: * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
0033: * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
0034: * POSSIBILITY OF SUCH DAMAGES.
0035: *
0036: * You acknowledge that this software is not designed, licensed or
0037: * intended for use in the design, construction, operation or
0038: * maintenance of any nuclear facility.
0039: *
0040: * $Revision: 1.5 $
0041: * $Date: 2007/02/09 17:20:10 $
0042: * $State: Exp $
0043: */
0044:
0045: package com.sun.j3d.loaders.objectfile;
0046:
0047: import com.sun.j3d.loaders.Scene;
0048: import com.sun.j3d.loaders.SceneBase;
0049: import com.sun.j3d.loaders.Loader;
0050: import com.sun.j3d.loaders.IncorrectFormatException;
0051: import com.sun.j3d.loaders.ParsingErrorException;
0052: import com.sun.j3d.loaders.objectfile.ObjectFileParser;
0053: import com.sun.j3d.loaders.objectfile.ObjectFileMaterials;
0054: import com.sun.j3d.utils.geometry.GeometryInfo;
0055: import com.sun.j3d.utils.geometry.NormalGenerator;
0056: import com.sun.j3d.utils.geometry.Stripifier;
0057: import java.io.FileNotFoundException;
0058: import java.io.StreamTokenizer;
0059: import java.io.Reader;
0060: import java.io.BufferedReader;
0061: import java.io.BufferedInputStream;
0062: import java.io.FileReader;
0063: import java.io.InputStreamReader;
0064: import java.io.IOException;
0065: import java.net.URL;
0066: import java.util.ArrayList;
0067: import java.util.Iterator;
0068: import java.util.HashMap;
0069: import java.util.StringTokenizer;
0070: import javax.media.j3d.*;
0071: import javax.vecmath.Color3f;
0072: import javax.vecmath.Point3f;
0073: import javax.vecmath.Vector3f;
0074: import javax.vecmath.TexCoord2f;
0075: import java.net.MalformedURLException;
0076:
0077: /**
0078: * The ObjectFile class implements the Loader interface for the Wavefront
0079: * .obj file format, a standard 3D object file format created for use with
0080: * Wavefront's Advanced Visualizer (tm) and available for purchase from
0081: * Viewpoint DataLabs, as well as other 3D model companies. Object Files
0082: * are text based
0083: * files supporting both polygonal and free-form geometry (curves
0084: * and surfaces). The Java 3D .obj file loader supports a subset of the
0085: * file format, but it is enough to load almost all commonly available
0086: * Object Files. Free-form geometry is not supported.</p>
0087: *
0088: * The Object File tokens currently supported by this loader are:</p>
0089: * <code>v <i>float</i> <i>float</i> <i>float</i></code></p>
0090: * <dl><dd>A single vertex's geometric position in space. The first vertex
0091: * listed in the file has index 1,
0092: * and subsequent vertices are numbered sequentially.</dl></p>
0093: * <code>vn <i>float</i> <i>float</i> <i>float</i></code></p>
0094: * <dl><dd>A normal. The first normal in the file is index 1, and
0095: * subsequent normals are numbered sequentially.</dl></p>
0096: * <code>vt <i>float</i> <i>float</i></code></p>
0097: * <dl><dd>A texture coordinate. The first texture coordinate in the file is
0098: * index 1, and subsequent normals are numbered sequentially.</dl></p>
0099: * <code>f <i>int</i> <i>int</i> <i>int</i> . . .</code></p>
0100: * <dl><dd><i><b>or</b></i></dl></p>
0101: * <code>f <i>int</i>/<i>int</i> <i>int</i>/<i>int</i> <i>int</i>/<i>int</i> . . .</code></p>
0102: * <dl><dd><i><b>or</i></b></dl></p>
0103: * <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>
0104: * <dl><dd>A polygonal face. The numbers are indexes into the arrays of
0105: * vertex positions, texture coordinates, and normals respectively.
0106: * There is no maximum number of vertices that a single polygon may
0107: * contain. The .obj file specification says that each face must
0108: * be flat and convex, but if the TRIANGULATE flag is sent to the
0109: * ObjectFile constructor, each face will be triangulated by the
0110: * Java 3D Triangulator, and therefore may be concave.
0111: * A number may be omitted if, for example, texture coordinates are
0112: * not being defined in the model. Numbers are normally positive
0113: * indexes, but may also be negative. An index of -1 means the last
0114: * member added to the respective array, -2 is the one before that,
0115: * and so on.</dl></p>
0116: * <code>g <i>name</i></code></p>
0117: * <dl><dd>Faces defined after this token will be added to the named group.
0118: * These geometry groups are returned as separated Shape3D objects
0119: * attached to the parent SceneGroup. Each named Shape3D will also
0120: * be in the Hashtable returned by Scene.getNamedObjects(). It is
0121: * legal to add faces to a group, switch to another group, and then
0122: * add more faces to the original group by reissuing the same name
0123: * with the g token. If faces are added to the model before the g
0124: * token is seen, the faces are put into the default group called
0125: * "default."</dl></p>
0126: * <code>s <i>int</i></code></p>
0127: * <dl><dd><i><b>or</i></b></dl></p>
0128: * <code>s off</code></p>
0129: * <dl><dd>If the vn token is not used in the file to specify vertex normals
0130: * for the model, this token may be used to put faces into groups
0131: * for normal calculation ("smoothing groups") in the same manner as
0132: * the 'g' token
0133: * is used to group faces geometrically. Faces in the same smoothing
0134: * group will have their normals calculated as if they are part of
0135: * the same smooth surface. To do this, we use the Java 3D NormalGenerator
0136: * utility with the creaseAngle parameter set to PI (180 degrees -
0137: * smooth shading, no creases) or to whatever the user has set the
0138: * creaseAngle. Faces in group 0 or 'off' use a
0139: * creaseAngle of zero, meaning there is no smoothing (the normal
0140: * of the face is used at all vertices giving the surface a faceted
0141: * look; there will be a
0142: * crease, or "Hard Edge," between each face in group zero). There is
0143: * also an implied hard edge <i>between</i> each smoothing group, where they
0144: * meet each other.</p>
0145: * </p>
0146: * If neither the vn nor the s token is used in the file, then normals
0147: * are calculated using the creaseAngle set in the contructor.
0148: * Normals are calculated on each geometry
0149: * group separately, meaning there will be a hard edge between each
0150: * geometry group.</dl></p>
0151: * </p>
0152: * <code>usemtl <i>name</i></code></p>
0153: * <dl><dd>The current (and subsequent) geometry groups (specified with
0154: * the 'g' token) have applied
0155: * to them the named material property. The following set of material
0156: * properties are available by default:</dl></p>
0157: * <pre>
0158: * amber amber_trans aqua aqua_filter
0159: * archwhite archwhite2 bflesh black
0160: * blondhair blue_pure bluegrey bluetint
0161: * blugrn blutan bluteal bone
0162: * bone1 bone2 brass brnhair
0163: * bronze brown brownlips brownskn
0164: * brzskin chappie charcoal deepgreen
0165: * default dkblue dkblue_pure dkbrown
0166: * dkdkgrey dkgreen dkgrey dkorange
0167: * dkpurple dkred dkteal emerald
0168: * fgreen flaqua flblack flblonde
0169: * flblue_pure flbrown fldkblue_pure fldkdkgrey
0170: * fldkgreen fldkgreen2 fldkgrey fldkolivegreen
0171: * fldkpurple fldkred flesh fleshtransparent
0172: * flgrey fllime flltbrown flltgrey
0173: * flltolivegreen flmintgreen flmustard florange
0174: * flpinegreen flpurple flred fltan
0175: * flwhite flwhite1 flyellow glass
0176: * glassblutint glasstransparent gold green
0177: * greenskn grey hair iris
0178: * jetflame lavendar lcdgreen lighttan
0179: * lighttan2 lighttan3 lighttannew lightyellow
0180: * lime lips ltbrown ltgrey
0181: * meh metal mintgrn muscle
0182: * navy_blue offwhite.cool offwhite.warm olivegreen
0183: * orange pale_green pale_pink pale_yellow
0184: * peach periwinkle pink pinktan
0185: * plasma purple red redbrick
0186: * redbrown redorange redwood rubber
0187: * ruby sand_stone sapphire shadow
0188: * ship2 silver skin sky_blue
0189: * smoked_glass tan taupe teeth
0190: * violet white yellow yellow_green
0191: * yellowbrt yelloworng
0192: * </pre>
0193: * <code>mtllib <i>filename</i></code></p>
0194: * <dl><dd>Load material properties from the named file. Materials
0195: * with the same name as the predefined materials above will override
0196: * the default value. Any directory path information in (filename)
0197: * is ignored. The .mtl files are assumed to be in the same directory
0198: * as the .obj file. If they are in a different directory, use
0199: * Loader.setBasePath() (or Loader.setBaseUrl() ). The format of the
0200: * material properties files
0201: * are as follows:</p>
0202: * <code>newmtl <i>name</i></code></p>
0203: * <dl><dd>Start the definition of a new named material property.</dl></p>
0204: * <code>Ka <i>float</i> <i>float</i> <i>float</i></code></p>
0205: * <dl><dd>Ambient color.</dl></p>
0206: * <code>Kd <i>float</i> <i>float</i> <i>float</i></code></p>
0207: * <dl><dd>Diffuse color.</dl></p>
0208: * <code>Ks <i>float</i> <i>float</i> <i>float</i></code></p>
0209: * <dl><dd>Specular color.</dl></p>
0210: * <code>illum <i>(0, 1, or 2)</i></code></p>
0211: * <dl><dd>0 to disable lighting, 1 for ambient & diffuse only (specular
0212: * color set to black), 2 for full lighting.</dl></p>
0213: * <code>Ns <i>float</i></code></p>
0214: * <dl><dd>Shininess (clamped to 1.0 - 128.0).</dl></p>
0215: * <code>map_Kd <i>filename</i></code></p>
0216: * <dl><dd>Texture map. Supports .rgb, .rgba, .int, .inta, .sgi, and
0217: * .bw files in addition to those supported by
0218: * <a href="../../utils/image/TextureLoader.html">TextureLoader</a>.
0219: * </dl></dl></p>
0220: */
0221:
0222: public class ObjectFile implements Loader {
0223: // 0=Input file assumed good
0224: // 1=Input file checked for inconsistencies
0225: // 2=path names
0226: // 4=flags
0227: // 8=Timing Info
0228: // 16=Tokens
0229: // 32=Token details (use with 16)
0230: // 64=limits of model coordinates
0231: private static final int DEBUG = 0;
0232:
0233: /**
0234: * Flag sent to constructor. The object's vertices will be changed
0235: * so that the object is centered at (0,0,0) and the coordinate
0236: * positions are all in the range of (-1,-1,-1) to (1,1,1).
0237: */
0238: public static final int RESIZE = LOAD_SOUND_NODES << 1;
0239:
0240: /**
0241: * Flag sent to constructor. The Shape3D object will be created
0242: * by using the GeometryInfo POLYGON_ARRAY primitive, causing
0243: * them to be Triangulated by GeometryInfo. Use
0244: * this if you suspect concave or other non-behaving polygons
0245: * in your model.
0246: */
0247: public static final int TRIANGULATE = RESIZE << 1;
0248:
0249: /**
0250: * Flag sent to constructor. Use if the vertices in your .obj
0251: * file were specified with clockwise winding (Java 3D wants
0252: * counter-clockwise) so you see the back of the polygons and
0253: * not the front. Calls GeometryInfo.reverse().
0254: */
0255: public static final int REVERSE = TRIANGULATE << 1;
0256:
0257: /**
0258: * Flag sent to contructor. After normals are generated the data
0259: * will be analyzed to find triangle strips. Use this if your
0260: * hardware supports accelerated rendering of strips.
0261: */
0262: public static final int STRIPIFY = REVERSE << 1;
0263:
0264: private static final char BACKSLASH = '\\';
0265:
0266: private int flags;
0267: private String basePath = null;
0268: private URL baseUrl = null;
0269: private boolean fromUrl = false;
0270: private float radians;
0271:
0272: // First, lists of points are read from the .obj file into these arrays. . .
0273: private ArrayList coordList; // Holds Point3f
0274: private ArrayList texList; // Holds TexCoord2f
0275: private ArrayList normList; // Holds Vector3f
0276:
0277: // . . . and index lists are read into these arrays.
0278: private ArrayList coordIdxList; // Holds Integer index into coordList
0279: private ArrayList texIdxList; // Holds Integer index into texList
0280: private ArrayList normIdxList; // Holds Integer index into normList
0281:
0282: // The length of each face is stored in this array.
0283: private ArrayList stripCounts; // Holds Integer
0284:
0285: // Each face's Geometry Group membership is kept here. . .
0286: private HashMap groups; // key=Integer index into stripCounts
0287: // value=String name of group
0288: private String curGroup;
0289:
0290: // . . . and Smoothing Group membership is kept here
0291: private HashMap sGroups; // key=Integer index into stripCounts
0292: // value=String name of group
0293: private String curSgroup;
0294:
0295: // The name of each group's "usemtl" material property is kept here
0296: private HashMap groupMaterials; // key=String name of Group
0297: // value=String name of material
0298:
0299: // After reading the entire file, the faces are converted into triangles.
0300: // The Geometry Group information is converted into these structures. . .
0301: private HashMap triGroups; // key=String name of group
0302: // value=ArrayList of Integer
0303: // indices into coordIdxList
0304: private ArrayList curTriGroup;
0305:
0306: // . . . and Smoothing Group info is converted into these.
0307: private HashMap triSgroups; // key=String name of group
0308: // value=ArrayList of Integer
0309: // indices into coordIdxList
0310: private ArrayList curTriSgroup;
0311:
0312: // Finally, coordList, texList, and normList are converted to arrays for
0313: // use with GeometryInfo
0314: private Point3f coordArray[] = null;
0315: private Vector3f normArray[] = null;
0316: private TexCoord2f texArray[] = null;
0317:
0318: // Used for debugging
0319: private long time;
0320:
0321: private ObjectFileMaterials materials = null;
0322:
0323: void readVertex(ObjectFileParser st) throws ParsingErrorException {
0324: Point3f p = new Point3f();
0325:
0326: st.getNumber();
0327: p.x = (float) st.nval;
0328: st.getNumber();
0329: p.y = (float) st.nval;
0330: st.getNumber();
0331: p.z = (float) st.nval;
0332:
0333: if ((DEBUG & 32) != 0)
0334: System.out.println(" (" + p.x + "," + p.y + "," + p.z
0335: + ")");
0336:
0337: st.skipToNextLine();
0338:
0339: // Add this vertex to the array
0340: coordList.add(p);
0341: } // End of readVertex
0342:
0343: /**
0344: * readNormal
0345: */
0346: void readNormal(ObjectFileParser st) throws ParsingErrorException {
0347: Vector3f p = new Vector3f();
0348:
0349: st.getNumber();
0350: p.x = (float) st.nval;
0351: st.getNumber();
0352: p.y = (float) st.nval;
0353: st.getNumber();
0354: p.z = (float) st.nval;
0355:
0356: if ((DEBUG & 32) != 0)
0357: System.out.println(" (" + p.x + "," + p.y + "," + p.z
0358: + ")");
0359:
0360: st.skipToNextLine();
0361:
0362: // Add this vertex to the array
0363: normList.add(p);
0364: } // End of readNormal
0365:
0366: /**
0367: * readTexture
0368: */
0369: void readTexture(ObjectFileParser st) throws ParsingErrorException {
0370: TexCoord2f p = new TexCoord2f();
0371:
0372: st.getNumber();
0373: p.x = (float) st.nval;
0374: st.getNumber();
0375: p.y = (float) st.nval;
0376:
0377: if ((DEBUG & 32) != 0)
0378: System.out.println(" (" + p.x + "," + p.y + ")");
0379:
0380: st.skipToNextLine();
0381:
0382: // Add this vertex to the array
0383: texList.add(p);
0384: } // End of readTexture
0385:
0386: /**
0387: * readFace
0388: *
0389: * Adds the indices of the current face to the arrays.
0390: *
0391: * ViewPoint files can have up to three arrays: Vertex Positions,
0392: * Texture Coordinates, and Vertex Normals. Each vertex can
0393: * contain indices into all three arrays.
0394: */
0395: void readFace(ObjectFileParser st) throws ParsingErrorException {
0396: int vertIndex, texIndex = 0, normIndex = 0;
0397: int count = 0;
0398:
0399: // There are n vertices on each line. Each vertex is comprised
0400: // of 1-3 numbers separated by slashes ('/'). The slashes may
0401: // be omitted if there's only one number.
0402:
0403: st.getToken();
0404:
0405: while (st.ttype != st.TT_EOL) {
0406: // First token is always a number (or EOL)
0407: st.pushBack();
0408: st.getNumber();
0409: vertIndex = (int) st.nval - 1;
0410: if (vertIndex < 0)
0411: vertIndex += coordList.size() + 1;
0412: coordIdxList.add(new Integer(vertIndex));
0413:
0414: // Next token is a slash, a number, or EOL. Continue on slash
0415: st.getToken();
0416: if (st.ttype == '/') {
0417:
0418: // If there's a number after the first slash, read it
0419: st.getToken();
0420: if (st.ttype == st.TT_WORD) {
0421: // It's a number
0422: st.pushBack();
0423: st.getNumber();
0424: texIndex = (int) st.nval - 1;
0425: if (texIndex < 0)
0426: texIndex += texList.size() + 1;
0427: texIdxList.add(new Integer(texIndex));
0428: st.getToken();
0429: }
0430:
0431: // Next token is a slash, a number, or EOL. Continue on slash
0432: if (st.ttype == '/') {
0433:
0434: // There has to be a number after the 2nd slash
0435: st.getNumber();
0436: normIndex = (int) st.nval - 1;
0437: if (normIndex < 0)
0438: normIndex += normList.size() + 1;
0439: normIdxList.add(new Integer(normIndex));
0440: st.getToken();
0441: }
0442: }
0443: if ((DEBUG & 32) != 0) {
0444: System.out.println(" " + vertIndex + '/' + texIndex
0445: + '/' + normIndex);
0446: }
0447: count++;
0448: }
0449:
0450: Integer faceNum = new Integer(stripCounts.size());
0451: stripCounts.add(new Integer(count));
0452:
0453: // Add face to current groups
0454: groups.put(faceNum, curGroup);
0455: if (curSgroup != null)
0456: sGroups.put(faceNum, curSgroup);
0457:
0458: // In case we exited early
0459: st.skipToNextLine();
0460: } // End of readFace
0461:
0462: /**
0463: * readPartName
0464: */
0465: void readPartName(ObjectFileParser st) {
0466: st.getToken();
0467:
0468: // Find the Material Property of the current group
0469: String curMat = (String) groupMaterials.get(curGroup);
0470:
0471: // New faces will be added to the curGroup
0472: if (st.ttype != ObjectFileParser.TT_WORD)
0473: curGroup = "default";
0474: else
0475: curGroup = st.sval;
0476: if ((DEBUG & 32) != 0)
0477: System.out.println(" Changed to group " + curGroup);
0478:
0479: // See if this group has Material Properties yet
0480: if (groupMaterials.get(curGroup) == null) {
0481: // It doesn't - carry over from last group
0482: groupMaterials.put(curGroup, curMat);
0483: }
0484:
0485: st.skipToNextLine();
0486: } // End of readPartName
0487:
0488: /**
0489: * readMaterialName
0490: */
0491: void readMaterialName(ObjectFileParser st)
0492: throws ParsingErrorException {
0493: st.getToken();
0494: if (st.ttype == ObjectFileParser.TT_WORD) {
0495: groupMaterials.put(curGroup, new String(st.sval));
0496: if ((DEBUG & 32) != 0) {
0497: System.out.println(" Material Property " + st.sval
0498: + " assigned to group " + curGroup);
0499: }
0500: }
0501: st.skipToNextLine();
0502: } // End of readMaterialName
0503:
0504: /**
0505: * loadMaterialFile
0506: *
0507: * Both types of slashes are returned as tokens from our parser,
0508: * so we go through the line token by token and keep just the
0509: * last token on the line. This should be the filename without
0510: * any directory info.
0511: */
0512: void loadMaterialFile(ObjectFileParser st)
0513: throws ParsingErrorException {
0514: String s = null;
0515:
0516: // Filenames are case sensitive
0517: st.lowerCaseMode(false);
0518:
0519: // Get name of material file (skip path)
0520: do {
0521: st.getToken();
0522: if (st.ttype == ObjectFileParser.TT_WORD)
0523: s = st.sval;
0524: } while (st.ttype != ObjectFileParser.TT_EOL);
0525:
0526: materials.readMaterialFile(fromUrl, fromUrl ? baseUrl
0527: .toString() : basePath, s);
0528:
0529: st.lowerCaseMode(true);
0530: st.skipToNextLine();
0531: } // End of loadMaterialFile
0532:
0533: /**
0534: * readSmoothingGroup
0535: */
0536: void readSmoothingGroup(ObjectFileParser st)
0537: throws ParsingErrorException {
0538: st.getToken();
0539: if (st.ttype != ObjectFileParser.TT_WORD) {
0540: st.skipToNextLine();
0541: return;
0542: }
0543: if (st.sval.equals("off"))
0544: curSgroup = "0";
0545: else
0546: curSgroup = st.sval;
0547: if ((DEBUG & 32) != 0)
0548: System.out.println(" Smoothing group " + curSgroup);
0549: st.skipToNextLine();
0550: } // End of readSmoothingGroup
0551:
0552: /**
0553: * readFile
0554: *
0555: * Read the model data from the file.
0556: */
0557: void readFile(ObjectFileParser st) throws ParsingErrorException {
0558: int t;
0559:
0560: st.getToken();
0561: while (st.ttype != ObjectFileParser.TT_EOF) {
0562:
0563: // Print out one token for each line
0564: if ((DEBUG & 16) != 0) {
0565: System.out.print("Token ");
0566: if (st.ttype == ObjectFileParser.TT_EOL)
0567: System.out.println("EOL");
0568: else if (st.ttype == ObjectFileParser.TT_WORD)
0569: System.out.println(st.sval);
0570: else
0571: System.out.println((char) st.ttype);
0572: }
0573:
0574: if (st.ttype == ObjectFileParser.TT_WORD) {
0575: if (st.sval.equals("v")) {
0576: readVertex(st);
0577: } else if (st.sval.equals("vn")) {
0578: readNormal(st);
0579: } else if (st.sval.equals("vt")) {
0580: readTexture(st);
0581: } else if (st.sval.equals("f")) {
0582: readFace(st);
0583: } else if (st.sval.equals("fo")) { // Not sure what the dif is
0584: readFace(st);
0585: } else if (st.sval.equals("g")) {
0586: readPartName(st);
0587: } else if (st.sval.equals("s")) {
0588: readSmoothingGroup(st);
0589: } else if (st.sval.equals("p")) {
0590: st.skipToNextLine();
0591: } else if (st.sval.equals("l")) {
0592: st.skipToNextLine();
0593: } else if (st.sval.equals("mtllib")) {
0594: loadMaterialFile(st);
0595: } else if (st.sval.equals("usemtl")) {
0596: readMaterialName(st);
0597: } else if (st.sval.equals("maplib")) {
0598: st.skipToNextLine();
0599: } else if (st.sval.equals("usemap")) {
0600: st.skipToNextLine();
0601: } else {
0602: throw new ParsingErrorException(
0603: "Unrecognized token, line " + st.lineno());
0604: }
0605: }
0606:
0607: st.skipToNextLine();
0608:
0609: // Get next token
0610: st.getToken();
0611: }
0612: } // End of readFile
0613:
0614: /**
0615: * Constructor.
0616: *
0617: * @param flags The constants from above or from
0618: * com.sun.j3d.loaders.Loader, possibly "or'ed" (|) together.
0619: * @param radians Ignored if the vn token is present in the model (user
0620: * normals supplied). Otherwise, crease angle to use within smoothing
0621: * groups, or within geometry groups if the s token isn't present either.
0622: */
0623: public ObjectFile(int flags, float radians) {
0624: setFlags(flags);
0625: this .radians = radians;
0626: } // End of ObjectFile(int, float)
0627:
0628: /**
0629: * Constructor. Crease Angle set to default of
0630: * 44 degrees (see NormalGenerator utility for details).
0631: * @param flags The constants from above or from
0632: * com.sun.j3d.loaders.Loader, possibly "or'ed" (|) together.
0633: */
0634: public ObjectFile(int flags) {
0635: this (flags, -1.0f);
0636: } // End of ObjectFile(int)
0637:
0638: /**
0639: * Default constructor. Crease Angle set to default of
0640: * 44 degrees (see NormalGenerator utility for details). Flags
0641: * set to zero (0).
0642: */
0643: public ObjectFile() {
0644: this (0, -1.0f);
0645: } // End of ObjectFile()
0646:
0647: /**
0648: * Takes a file name and sets the base path to the directory
0649: * containing that file.
0650: */
0651: private void setBasePathFromFilename(String fileName) {
0652: if (fileName.lastIndexOf(java.io.File.separator) == -1) {
0653: // No path given - current directory
0654: setBasePath("." + java.io.File.separator);
0655: } else {
0656: setBasePath(fileName.substring(0, fileName
0657: .lastIndexOf(java.io.File.separator)));
0658: }
0659: } // End of setBasePathFromFilename
0660:
0661: /**
0662: * The Object File is loaded from the .obj file specified by
0663: * the filename.
0664: * To attach the model to your scene, call getSceneGroup() on
0665: * the Scene object passed back, and attach the returned
0666: * BranchGroup to your scene graph. For an example, see
0667: * j3d-examples/ObjLoad/ObjLoad.java.
0668: */
0669: public Scene load(String filename) throws FileNotFoundException,
0670: IncorrectFormatException, ParsingErrorException {
0671:
0672: setBasePathFromFilename(filename);
0673:
0674: Reader reader = new BufferedReader(new FileReader(filename));
0675: return load(reader);
0676: } // End of load(String)
0677:
0678: private void setBaseUrlFromUrl(URL url)
0679: throws FileNotFoundException {
0680: String u = url.toString();
0681: String s;
0682: if (u.lastIndexOf('/') == -1) {
0683: s = url.getProtocol() + ":";
0684: } else {
0685: s = u.substring(0, u.lastIndexOf('/') + 1);
0686: }
0687: try {
0688: baseUrl = new URL(s);
0689: } catch (MalformedURLException e) {
0690: throw new FileNotFoundException(e.getMessage());
0691: }
0692: } // End of setBaseUrlFromUrl
0693:
0694: /**
0695: * The object file is loaded off of the web.
0696: * To attach the model to your scene, call getSceneGroup() on
0697: * the Scene object passed back, and attach the returned
0698: * BranchGroup to your scene graph. For an example, see
0699: * j3d-examples/ObjLoad/ObjLoad.java.
0700: */
0701: public Scene load(URL url) throws FileNotFoundException,
0702: IncorrectFormatException, ParsingErrorException {
0703: BufferedReader reader;
0704:
0705: if (baseUrl == null)
0706: setBaseUrlFromUrl(url);
0707:
0708: try {
0709: reader = new BufferedReader(new InputStreamReader(url
0710: .openStream()));
0711: } catch (IOException e) {
0712: throw new FileNotFoundException(e.getMessage());
0713: }
0714: fromUrl = true;
0715: return load(reader);
0716: } // End of load(URL)
0717:
0718: /**
0719: * getLimits
0720: *
0721: * Returns an array of Point3f which form a bounding box around the
0722: * object. Element 0 is the low value, element 1 is the high value.
0723: * See normalize() below for an example of how to use this method.
0724: */
0725: private Point3f[] getLimits() {
0726: Point3f cur_vtx = new Point3f();
0727:
0728: // Find the limits of the model
0729: Point3f[] limit = new Point3f[2];
0730: limit[0] = new Point3f(Float.MAX_VALUE, Float.MAX_VALUE,
0731: Float.MAX_VALUE);
0732: limit[1] = new Point3f(Float.MIN_VALUE, Float.MIN_VALUE,
0733: Float.MIN_VALUE);
0734: for (int i = 0; i < coordList.size(); i++) {
0735:
0736: cur_vtx = (Point3f) coordList.get(i);
0737:
0738: // Keep track of limits for normalization
0739: if (cur_vtx.x < limit[0].x)
0740: limit[0].x = cur_vtx.x;
0741: if (cur_vtx.x > limit[1].x)
0742: limit[1].x = cur_vtx.x;
0743: if (cur_vtx.y < limit[0].y)
0744: limit[0].y = cur_vtx.y;
0745: if (cur_vtx.y > limit[1].y)
0746: limit[1].y = cur_vtx.y;
0747: if (cur_vtx.z < limit[0].z)
0748: limit[0].z = cur_vtx.z;
0749: if (cur_vtx.z > limit[1].z)
0750: limit[1].z = cur_vtx.z;
0751: }
0752:
0753: if ((DEBUG & 64) != 0) {
0754: System.out.println("Model range: (" + limit[0].x + ","
0755: + limit[0].y + "," + limit[0].z + ") to ("
0756: + limit[1].x + "," + limit[1].y + "," + limit[1].z
0757: + ")");
0758: }
0759:
0760: return limit;
0761: } // End of getLimits
0762:
0763: /**
0764: * Center the object and make it (-1,-1,-1) to (1,1,1).
0765: */
0766: private void resize() {
0767: int i, j;
0768: Point3f cur_vtx = new Point3f();
0769: float biggest_dif;
0770:
0771: Point3f[] limit = getLimits();
0772:
0773: // Move object so it's centered on (0,0,0)
0774: Vector3f offset = new Vector3f(-0.5f
0775: * (limit[0].x + limit[1].x), -0.5f
0776: * (limit[0].y + limit[1].y), -0.5f
0777: * (limit[0].z + limit[1].z));
0778:
0779: if ((DEBUG & 64) != 0) {
0780: System.out.println("Offset amount: (" + offset.x + ","
0781: + offset.y + "," + offset.z + ")");
0782: }
0783:
0784: // Find the divide-by value for the normalization
0785: biggest_dif = limit[1].x - limit[0].x;
0786: if (biggest_dif < limit[1].y - limit[0].y)
0787: biggest_dif = limit[1].y - limit[0].y;
0788: if (biggest_dif < limit[1].z - limit[0].z)
0789: biggest_dif = limit[1].z - limit[0].z;
0790: biggest_dif /= 2.0f;
0791:
0792: for (i = 0; i < coordList.size(); i++) {
0793:
0794: cur_vtx = (Point3f) coordList.get(i);
0795:
0796: cur_vtx.add(cur_vtx, offset);
0797:
0798: cur_vtx.x /= biggest_dif;
0799: cur_vtx.y /= biggest_dif;
0800: cur_vtx.z /= biggest_dif;
0801:
0802: // coordList.setElementAt(cur_vtx, i);
0803: }
0804: } // End of resize
0805:
0806: private int[] objectToIntArray(ArrayList inList) {
0807: int outList[] = new int[inList.size()];
0808: for (int i = 0; i < inList.size(); i++) {
0809: outList[i] = ((Integer) inList.get(i)).intValue();
0810: }
0811: return outList;
0812: } // End of objectToIntArray
0813:
0814: private Point3f[] objectToPoint3Array(ArrayList inList) {
0815: Point3f outList[] = new Point3f[inList.size()];
0816: for (int i = 0; i < inList.size(); i++) {
0817: outList[i] = (Point3f) inList.get(i);
0818: }
0819: return outList;
0820: } // End of objectToPoint3Array
0821:
0822: private TexCoord2f[] objectToTexCoord2Array(ArrayList inList) {
0823: TexCoord2f outList[] = new TexCoord2f[inList.size()];
0824: for (int i = 0; i < inList.size(); i++) {
0825: outList[i] = (TexCoord2f) inList.get(i);
0826: }
0827: return outList;
0828: } // End of objectToTexCoord2Array
0829:
0830: private Vector3f[] objectToVectorArray(ArrayList inList) {
0831: Vector3f outList[] = new Vector3f[inList.size()];
0832: for (int i = 0; i < inList.size(); i++) {
0833: outList[i] = (Vector3f) inList.get(i);
0834: }
0835: return outList;
0836: } // End of objectToVectorArray
0837:
0838: /**
0839: * Each group is a list of indices into the model's index lists,
0840: * indicating the starting index of each triangle in the group.
0841: * This method converts those data structures
0842: * into an integer array to use with GeometryInfo.
0843: */
0844: private int[] groupIndices(ArrayList sourceList, ArrayList group) {
0845: int indices[] = new int[group.size() * 3];
0846: for (int i = 0; i < group.size(); i++) {
0847: int j = ((Integer) group.get(i)).intValue();
0848: indices[i * 3 + 0] = ((Integer) sourceList.get(j + 0))
0849: .intValue();
0850: indices[i * 3 + 1] = ((Integer) sourceList.get(j + 1))
0851: .intValue();
0852: indices[i * 3 + 2] = ((Integer) sourceList.get(j + 2))
0853: .intValue();
0854: }
0855: return indices;
0856: } // end of groupIndices
0857:
0858: /**
0859: * smoothingGroupNormals
0860: *
0861: * Smoothing groups are groups of faces who should be grouped
0862: * together for normal calculation purposes. The faces are
0863: * put into a GeometryInfo object and normals are calculated
0864: * with a 180 degree creaseAngle (no creases) or whatever the
0865: * user has specified. The normals
0866: * are then copied out of the GeometryInfo and back into
0867: * ObjectFile data structures.
0868: */
0869: private void smoothingGroupNormals() {
0870: NormalGenerator ng = new NormalGenerator(
0871: radians == -1.0f ? Math.PI : radians);
0872: NormalGenerator ng0 = new NormalGenerator(0.0);
0873: normList.clear();
0874: normIdxList = null;
0875: int newNormIdxArray[] = new int[coordIdxList.size()];
0876:
0877: Iterator e = triSgroups.keySet().iterator();
0878: while (e.hasNext()) {
0879: String curname = (String) e.next();
0880: ArrayList triList = (ArrayList) triSgroups.get(curname);
0881:
0882: // Check for group with no faces
0883: if (triList.size() > 0) {
0884:
0885: GeometryInfo gi = new GeometryInfo(
0886: GeometryInfo.TRIANGLE_ARRAY);
0887:
0888: gi.setCoordinateIndices(groupIndices(coordIdxList,
0889: triList));
0890: gi.setCoordinates(coordArray);
0891:
0892: if (curname.equals("0"))
0893: ng0.generateNormals(gi);
0894: else
0895: ng.generateNormals(gi);
0896:
0897: // Get the generated normals and indices
0898: Vector3f genNorms[] = gi.getNormals();
0899: int genNormIndices[] = gi.getNormalIndices();
0900:
0901: // Now we need to copy the generated normals into ObjectFile
0902: // data structures (normList and normIdxList). The variable
0903: // normIdx is the index of the index of the normal currently
0904: // being put into the list. It takes some calculation to
0905: // figure out the new index and where to put it.
0906: int normIdx = 0;
0907: // Repeat for each triangle in the smoothing group
0908: for (int i = 0; i < triList.size(); i++) {
0909:
0910: // Get the coordIdxList index of the first index in this face
0911: int idx = ((Integer) triList.get(i)).intValue();
0912:
0913: // Repeat for each vertex in the triangle
0914: for (int j = 0; j < 3; j++) {
0915:
0916: // Put the new normal's index into the index list
0917: newNormIdxArray[idx + j] = normList.size();
0918:
0919: // Add the vertex's normal to the normal list
0920: normList
0921: .add(genNorms[genNormIndices[normIdx++]]);
0922: }
0923: }
0924: }
0925: }
0926: normIdxList = new ArrayList(coordIdxList.size());
0927: for (int i = 0; i < coordIdxList.size(); i++) {
0928: normIdxList.add(new Integer(newNormIdxArray[i]));
0929: }
0930: normArray = objectToVectorArray(normList);
0931: } // end of smoothingGroupNormals
0932:
0933: /**
0934: * Each face is converted to triangles. As each face is converted,
0935: * we look up which geometry group and smoothing group the face
0936: * belongs to. The generated triangles are added to each of these
0937: * groups, which are also being converted to a new triangle based format.
0938: *
0939: * We need to convert to triangles before normals are generated
0940: * because of smoothing groups. The faces in a smoothing group
0941: * are copied into a GeometryInfo to have their normals calculated,
0942: * and then the normals are copied out of the GeometryInfo using
0943: * GeometryInfo.getNormalIndices. As part of Normal generation,
0944: * the geometry gets converted to Triangles. So we need to convert
0945: * to triangles *before* Normal generation so that the normals we
0946: * read out of the GeometryInfo match up with the vertex data
0947: * that we sent in. If we sent in TRIANGLE_FAN data, the normal
0948: * generator would convert it to triangles and we'd read out
0949: * normals formatted for Triangle data. This would not match up
0950: * with our original Fan data, so we couldn't tell which normals
0951: * go with which vertices.
0952: */
0953: private void convertToTriangles() {
0954: boolean triangulate = (flags & TRIANGULATE) != 0;
0955: boolean textures = !texList.isEmpty() && !texIdxList.isEmpty()
0956: && (texIdxList.size() == coordIdxList.size());
0957: boolean normals = !normList.isEmpty() && !normIdxList.isEmpty()
0958: && (normIdxList.size() == coordIdxList.size());
0959: int numFaces = stripCounts.size();
0960: boolean haveSgroups = curSgroup != null;
0961:
0962: triGroups = new HashMap(50);
0963: if (haveSgroups)
0964: triSgroups = new HashMap(50);
0965:
0966: ArrayList newCoordIdxList = null;
0967: ArrayList newTexIdxList = null;
0968: ArrayList newNormIdxList = null;
0969:
0970: if (triangulate) {
0971: GeometryInfo gi = new GeometryInfo(
0972: GeometryInfo.POLYGON_ARRAY);
0973: gi.setStripCounts(objectToIntArray(stripCounts));
0974: gi.setCoordinates(coordArray);
0975: gi.setCoordinateIndices(objectToIntArray(coordIdxList));
0976: if (textures) {
0977: gi.setTextureCoordinateParams(1, 2);
0978: gi.setTextureCoordinates(0, texArray);
0979: gi.setTextureCoordinateIndices(0,
0980: objectToIntArray(texIdxList));
0981: }
0982: if (normals) {
0983: gi.setNormals(normArray);
0984: gi.setNormalIndices(objectToIntArray(normIdxList));
0985: }
0986: gi.convertToIndexedTriangles();
0987:
0988: // Data is now indexed triangles. Next step is to take the data
0989: // out of the GeometryInfo and put into internal data structures
0990:
0991: int coordIndicesArray[] = gi.getCoordinateIndices();
0992:
0993: // Fix for #4366060
0994: // Make sure triangulated geometry has the correct number of triangles
0995: int tris = 0;
0996: for (int i = 0; i < numFaces; i++)
0997: tris += ((Integer) stripCounts.get(i)).intValue() - 2;
0998:
0999: if (coordIndicesArray.length != (tris * 3)) {
1000: // Model contains bad polygons that didn't triangulate into the
1001: // correct number of triangles. Fall back to "simple" triangulation
1002: triangulate = false;
1003: } else {
1004:
1005: int texIndicesArray[] = gi
1006: .getTextureCoordinateIndices();
1007: int normIndicesArray[] = gi.getNormalIndices();
1008:
1009: // Convert index arrays to internal ArrayList format
1010: coordIdxList.clear();
1011: texIdxList.clear();
1012: normIdxList.clear();
1013: for (int i = 0; i < coordIndicesArray.length; i++) {
1014: coordIdxList.add(new Integer(coordIndicesArray[i]));
1015: if (textures)
1016: texIdxList.add(new Integer(texIndicesArray[i]));
1017: if (normals)
1018: normIdxList
1019: .add(new Integer(normIndicesArray[i]));
1020: }
1021: }
1022: }
1023:
1024: if (!triangulate) {
1025: newCoordIdxList = new ArrayList();
1026: if (textures)
1027: newTexIdxList = new ArrayList();
1028: if (normals)
1029: newNormIdxList = new ArrayList();
1030: }
1031:
1032: // Repeat for each face in the model - add the triangles from each
1033: // face to the Geometry and Smoothing Groups
1034: int baseVertex = 0;
1035: for (int f = 0; f < numFaces; f++) {
1036: int faceSize = ((Integer) stripCounts.get(f)).intValue();
1037:
1038: // Find out the name of the group to which this face belongs
1039: Integer curFace = new Integer(f);
1040: curGroup = (String) groups.get(curFace);
1041:
1042: // Change to a new geometry group, create if it doesn't exist
1043: curTriGroup = (ArrayList) triGroups.get(curGroup);
1044: if (curTriGroup == null) {
1045: curTriGroup = new ArrayList();
1046: triGroups.put(curGroup, curTriGroup);
1047: }
1048:
1049: // Change to a new smoothing group, create if it doesn't exist
1050: if (haveSgroups) {
1051: curSgroup = (String) sGroups.get(curFace);
1052: if (curSgroup == null) {
1053: // Weird case - this face has no smoothing group. Happens if the
1054: // first 's' token comes after some faces have already been defined.
1055: // Assume they wanted no smoothing for these faces
1056: curSgroup = "0";
1057: }
1058: curTriSgroup = (ArrayList) triSgroups.get(curSgroup);
1059: if (curTriSgroup == null) {
1060: curTriSgroup = new ArrayList();
1061: triSgroups.put(curSgroup, curTriSgroup);
1062: }
1063: }
1064:
1065: if (triangulate) {
1066:
1067: // Each polygon of n vertices is now n-2 triangles
1068: for (int t = 0; t < faceSize - 2; t++) {
1069:
1070: // The groups just remember the first vertex of each triangle
1071: Integer triBaseVertex = new Integer(baseVertex);
1072: curTriGroup.add(triBaseVertex);
1073: if (haveSgroups)
1074: curTriSgroup.add(triBaseVertex);
1075:
1076: baseVertex += 3;
1077: }
1078: } else {
1079: // Triangulate simply
1080: for (int v = 0; v < faceSize - 2; v++) {
1081: // Add this triangle to the geometry group and the smoothing group
1082: Integer triBaseVertex = new Integer(newCoordIdxList
1083: .size());
1084: curTriGroup.add(triBaseVertex);
1085: if (haveSgroups)
1086: curTriSgroup.add(triBaseVertex);
1087:
1088: newCoordIdxList.add(coordIdxList.get(baseVertex));
1089: newCoordIdxList.add(coordIdxList.get(baseVertex + v
1090: + 1));
1091: newCoordIdxList.add(coordIdxList.get(baseVertex + v
1092: + 2));
1093:
1094: if (textures) {
1095: newTexIdxList.add(texIdxList.get(baseVertex));
1096: newTexIdxList.add(texIdxList.get(baseVertex + v
1097: + 1));
1098: newTexIdxList.add(texIdxList.get(baseVertex + v
1099: + 2));
1100: }
1101:
1102: if (normals) {
1103: newNormIdxList.add(normIdxList.get(baseVertex));
1104: newNormIdxList.add(normIdxList.get(baseVertex
1105: + v + 1));
1106: newNormIdxList.add(normIdxList.get(baseVertex
1107: + v + 2));
1108: }
1109: }
1110: baseVertex += faceSize;
1111: }
1112: }
1113:
1114: // No need to keep these around
1115: stripCounts = null;
1116: groups = null;
1117: sGroups = null;
1118:
1119: if (!triangulate) {
1120: coordIdxList = newCoordIdxList;
1121: texIdxList = newTexIdxList;
1122: normIdxList = newNormIdxList;
1123: }
1124: } // End of convertToTriangles
1125:
1126: private SceneBase makeScene() {
1127: // Create Scene to pass back
1128: SceneBase scene = new SceneBase();
1129: BranchGroup group = new BranchGroup();
1130: scene.setSceneGroup(group);
1131:
1132: boolean gen_norms = normList.isEmpty() || normIdxList.isEmpty()
1133: || (normIdxList.size() != coordIdxList.size());
1134: boolean do_tex = !texList.isEmpty() && !texIdxList.isEmpty()
1135: && (texIdxList.size() == coordIdxList.size());
1136:
1137: // Convert ArrayLists to arrays
1138: coordArray = objectToPoint3Array(coordList);
1139: if (!gen_norms)
1140: normArray = objectToVectorArray(normList);
1141: if (do_tex)
1142: texArray = objectToTexCoord2Array(texList);
1143:
1144: convertToTriangles();
1145:
1146: if ((DEBUG & 8) != 0) {
1147: time = System.currentTimeMillis() - time;
1148: System.out.println("Convert to triangles: " + time + " ms");
1149: time = System.currentTimeMillis();
1150: }
1151:
1152: if ((gen_norms) && (curSgroup != null)) {
1153: smoothingGroupNormals();
1154: gen_norms = false;
1155: if ((DEBUG & 8) != 0) {
1156: time = System.currentTimeMillis() - time;
1157: System.out.println("Smoothing group normals: " + time
1158: + " ms");
1159: time = System.currentTimeMillis();
1160: }
1161: }
1162:
1163: NormalGenerator ng = null;
1164: if (gen_norms)
1165: ng = new NormalGenerator(radians);
1166:
1167: Stripifier strippy = null;
1168: if ((flags & STRIPIFY) != 0)
1169: strippy = new Stripifier();
1170:
1171: long t1 = 0, t2 = 0, t3 = 0, t4 = 0;
1172:
1173: // Each "Group" of faces in the model will be one Shape3D
1174: Iterator e = triGroups.keySet().iterator();
1175: while (e.hasNext()) {
1176:
1177: String curname = (String) e.next();
1178: ArrayList triList = (ArrayList) triGroups.get(curname);
1179:
1180: // Check for group with no faces
1181: if (triList.size() > 0) {
1182:
1183: GeometryInfo gi = new GeometryInfo(
1184: GeometryInfo.TRIANGLE_ARRAY);
1185:
1186: gi.setCoordinateIndices(groupIndices(coordIdxList,
1187: triList));
1188: gi.setCoordinates(coordArray);
1189:
1190: if (do_tex) {
1191: gi.setTextureCoordinateParams(1, 2);
1192: gi.setTextureCoordinates(0, texArray);
1193: gi.setTextureCoordinateIndices(0, groupIndices(
1194: texIdxList, triList));
1195: }
1196:
1197: if ((DEBUG & 8) != 0)
1198: time = System.currentTimeMillis();
1199: if (gen_norms) {
1200: if ((flags & REVERSE) != 0)
1201: gi.reverse();
1202: ng.generateNormals(gi);
1203: if ((DEBUG & 8) != 0) {
1204: t2 += System.currentTimeMillis() - time;
1205: System.out.println("Generate normals: " + t2
1206: + " ms");
1207: time = System.currentTimeMillis();
1208: }
1209: } else {
1210: gi.setNormalIndices(groupIndices(normIdxList,
1211: triList));
1212: gi.setNormals(normArray);
1213: if ((flags & REVERSE) != 0)
1214: gi.reverse();
1215: }
1216:
1217: if ((flags & STRIPIFY) != 0) {
1218: strippy.stripify(gi);
1219: if ((DEBUG & 8) != 0) {
1220: t3 += System.currentTimeMillis() - time;
1221: System.out.println("Stripify: " + t3 + " ms");
1222: time = System.currentTimeMillis();
1223: }
1224: }
1225:
1226: // Put geometry into Shape3d
1227: Shape3D shape = new Shape3D();
1228:
1229: shape.setGeometry(gi
1230: .getGeometryArray(true, true, false));
1231:
1232: String matName = (String) groupMaterials.get(curname);
1233: materials.assignMaterial(matName, shape);
1234:
1235: group.addChild(shape);
1236: scene.addNamedObject(curname, shape);
1237:
1238: if ((DEBUG & 8) != 0) {
1239: t4 += System.currentTimeMillis() - time;
1240: System.out.println("Shape 3D: " + t4 + " ms");
1241: time = System.currentTimeMillis();
1242: }
1243: }
1244: }
1245:
1246: return scene;
1247: } // end of makeScene
1248:
1249: /**
1250: * The Object File is loaded from the already opened file.
1251: * To attach the model to your scene, call getSceneGroup() on
1252: * the Scene object passed back, and attach the returned
1253: * BranchGroup to your scene graph. For an example, see
1254: * j3d-examples/ObjLoad/ObjLoad.java.
1255: */
1256: public Scene load(Reader reader) throws FileNotFoundException,
1257: IncorrectFormatException, ParsingErrorException {
1258: // ObjectFileParser does lexical analysis
1259: ObjectFileParser st = new ObjectFileParser(reader);
1260:
1261: coordList = new ArrayList();
1262: texList = new ArrayList();
1263: normList = new ArrayList();
1264: coordIdxList = new ArrayList();
1265: texIdxList = new ArrayList();
1266: normIdxList = new ArrayList();
1267: groups = new HashMap(50);
1268: curGroup = "default";
1269: sGroups = new HashMap(50);
1270: curSgroup = null;
1271: stripCounts = new ArrayList();
1272: groupMaterials = new HashMap(50);
1273: groupMaterials.put(curGroup, "default");
1274: materials = new ObjectFileMaterials();
1275:
1276: time = 0L;
1277: if ((DEBUG & 8) != 0) {
1278: time = System.currentTimeMillis();
1279: }
1280:
1281: readFile(st);
1282:
1283: if ((DEBUG & 8) != 0) {
1284: time = System.currentTimeMillis() - time;
1285: System.out.println("Read file: " + time + " ms");
1286: time = System.currentTimeMillis();
1287: }
1288:
1289: if ((flags & RESIZE) != 0)
1290: resize();
1291:
1292: return makeScene();
1293: } // End of load(Reader)
1294:
1295: /**
1296: * For an .obj file loaded from a URL, set the URL where associated files
1297: * (like material properties files) will be found.
1298: * Only needs to be called to set it to a different URL
1299: * from that containing the .obj file.
1300: */
1301: public void setBaseUrl(URL url) {
1302: baseUrl = url;
1303: } // End of setBaseUrl
1304:
1305: /**
1306: * Return the URL where files associated with this .obj file (like
1307: * material properties files) will be found.
1308: */
1309: public URL getBaseUrl() {
1310: return baseUrl;
1311: } // End of getBaseUrl
1312:
1313: /**
1314: * Set the path where files associated with this .obj file are
1315: * located.
1316: * Only needs to be called to set it to a different directory
1317: * from that containing the .obj file.
1318: */
1319: public void setBasePath(String pathName) {
1320: basePath = pathName;
1321: if (basePath == null || basePath == "")
1322: basePath = "." + java.io.File.separator;
1323: basePath = basePath.replace('/', java.io.File.separatorChar);
1324: basePath = basePath.replace('\\', java.io.File.separatorChar);
1325: if (!basePath.endsWith(java.io.File.separator))
1326: basePath = basePath + java.io.File.separator;
1327: } // End of setBasePath
1328:
1329: /**
1330: * Return the path where files associated with this .obj file (like material
1331: * files) are located.
1332: */
1333: public String getBasePath() {
1334: return basePath;
1335: } // End of getBasePath
1336:
1337: /**
1338: * Set parameters for loading the model.
1339: * Flags defined in Loader.java are ignored by the ObjectFile Loader
1340: * because the .obj file format doesn't include lights, fog, background,
1341: * behaviors, views, or sounds. However, several flags are defined
1342: * specifically for use with the ObjectFile Loader (see above).
1343: */
1344: public void setFlags(int flags) {
1345: this .flags = flags;
1346: if ((DEBUG & 4) != 0)
1347: System.out.println("Flags = " + flags);
1348: } // End of setFlags
1349:
1350: /**
1351: * Get the parameters currently defined for loading the model.
1352: * Flags defined in Loader.java are ignored by the ObjectFile Loader
1353: * because the .obj file format doesn't include lights, fog, background,
1354: * behaviors, views, or sounds. However, several flags are defined
1355: * specifically for use with the ObjectFile Loader (see above).
1356: */
1357: public int getFlags() {
1358: return flags;
1359: } // End of getFlags
1360:
1361: } // End of class ObjectFile
1362:
1363: // End of file ObjectFile.java
|