1 /*
2  * $RCSfile: LwoParser.java,v $
3  *
4  * Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * - Redistribution of source code must retain the above copyright
11  *   notice, this list of conditions and the following disclaimer.
12  *
13  * - Redistribution in binary form must reproduce the above copyright
14  *   notice, this list of conditions and the following disclaimer in
15  *   the documentation and/or other materials provided with the
16  *   distribution.
17  *
18  * Neither the name of Sun Microsystems, Inc. or the names of
19  * contributors may be used to endorse or promote products derived
20  * from this software without specific prior written permission.
21  *
22  * This software is provided "AS IS," without a warranty of any
23  * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND
24  * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY,
25  * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY
26  * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL
27  * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF
28  * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
29  * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR
30  * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL,
31  * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND
32  * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR
33  * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
34  * POSSIBILITY OF SUCH DAMAGES.
35  *
36  * You acknowledge that this software is not designed, licensed or
37  * intended for use in the design, construction, operation or
38  * maintenance of any nuclear facility.
39  *
40  * $Revision: 1.4 $
41  * $Date: 2007/02/09 17:20:07 $
42  * $State: Exp $
43  */
44 
45 package com.sun.j3d.loaders.lw3d;
46 
47 
48 
49 import java.io.File;
50 import java.io.FileNotFoundException;
51 import java.io.IOException;
52 import java.util.Vector;
53 import java.util.Date;
54 import java.util.Enumeration;
55 import com.sun.j3d.loaders.lw3d.LWOBFileReader;
56 import com.sun.j3d.internal.J3dUtilsI18N;
57 import java.net.*;
58 import com.sun.j3d.loaders.ParsingErrorException;
59 import com.sun.j3d.loaders.IncorrectFormatException;
60 
61 
62 /**
63  * This class is responsible for parsing a binary Object file and storing
64  * the data. This class is not called directly, but is the parent class of
65  * J3dLwoObject. The subclass calls this class to parse the file, then it
66  * turns the resulting data into Java3D objects. LwoObject instantiates
67  * an LWOBFileReader object to parse the data. Then the class creates a
68  * list of ShapeHolder objects to hold all of the vertex/facet data and
69  * surface references and creates a list of LwoSurface objects to hold
70  * the data for each surface.<BR>
71  * Rather than describe in detail how the file is parsed for each method,
72  * I advise the user of this code to understand the lw3d file format
73  * specs, which are pretty clear.
74  */
75 
76 class LwoParser extends ParserObject {
77 
78     LWOBFileReader theReader;
79     int currLength;
80     float coordsArray[];
81     float normalCoordsArray[];
82     int facetIndicesArray[];
83     int facetSizesArray[];
84     int normalIndicesArray[];
85     int red = 255, green = 255, blue = 255;
86     float diffuse = 0.0f, specular = 0.0f, transparency = 0.0f, luminosity = 0.0f;
87     int gloss = 128;
88     Vector surfNameList = null;
89     Vector surfaceList = new Vector(200);
90     Vector shapeList = new Vector(200);
91 
92 	/**
93 	* Constructor: Creates file reader and calls parseFile() to actually
94 	* read the file and grab the data
95 	*/
LwoParser(String fileName, int debugVals)96     LwoParser(String fileName, int debugVals)
97 	throws FileNotFoundException {
98 
99 	super(debugVals);
100 	debugOutputLn(TRACE, "parser()");
101 	long start = System.currentTimeMillis();
102 	theReader = new LWOBFileReader(fileName);
103 	debugOutputLn(TIME, " file opened in " +
104 		      (System.currentTimeMillis() - start));
105 	parseFile();
106     }
107 
LwoParser(URL url, int debugVals)108   LwoParser(URL url, int debugVals)
109     throws FileNotFoundException {
110       super(debugVals);
111       debugOutputLn(TRACE, "parser()");
112       try {
113 	long start = System.currentTimeMillis();
114 	theReader = new LWOBFileReader(url);
115 	debugOutputLn(TIME, " file opened in " +
116 		      (System.currentTimeMillis() - start));
117       }
118       catch (IOException ex) {
119 	throw new FileNotFoundException(url.toString());
120       }
121       parseFile();
122   }
123 
124 
125 	/**
126 	* Detail polygons are currently not implemented by this loader.  Their
127 	* structure in geometry files is a bit complex, so there's this separate
128 	* method for simply parsing through and ignoring the data for detail
129 	* polygons
130 	*/
skipDetailPolygons(int numPolys)131     int skipDetailPolygons(int numPolys) throws ParsingErrorException {
132 	debugOutputLn(TRACE, "skipDetailPolygons(), numPolys = " + numPolys);
133 	int lengthRead = 0;
134 	int vert;
135 
136 	try {
137 	    for (int polyNum = 0; polyNum < numPolys; ++polyNum) {
138 		debugOutputLn(VALUES, "polyNum = " + polyNum);
139 		int numVerts = theReader.getShortInt();
140 		theReader.skip(numVerts * 2 + 2);  // skip indices plus surf
141 		lengthRead += (numVerts * 2) + 4;  // increment counter
142 	    }
143 	}
144 	catch (IOException e) {
145 	    debugOutputLn(EXCEPTION, "Exception in reading detail polys: " + e);
146 	    throw new ParsingErrorException(e.getMessage());
147 	}
148 	return lengthRead;
149     }
150 
151 	/**
152 	* Returns already-existing ShapeHolder if one exists with the same
153 	* surface and the same geometry type (point, line, or poly)
154 	*/
getAppropriateShape(int numSurf, int numVerts)155     ShapeHolder getAppropriateShape(int numSurf, int numVerts) {
156 	for (Enumeration e = shapeList.elements();
157 	     e.hasMoreElements() ;) {
158 	    ShapeHolder shape = (ShapeHolder)e.nextElement();
159 	    if (shape.numSurf == numSurf)
160 		if (shape.numVerts == numVerts ||
161 		    (shape.numVerts > 3 &&
162 		     numVerts > 3))
163 		    return shape;
164 	}
165 	return null;
166     }
167 
168 
169 	/**
170 	* Parse the file for all the data for a POLS object (polygon
171 	* description)
172 	*/
getPols(int length)173     void getPols(int length) {
174 	debugOutputLn(TRACE, "getPols(len), len = " + length);
175 	int vert;
176 	int lengthRead = 0;
177 	int prevNumVerts = -1;
178 	int prevNumSurf = 0;
179 	Vector facetSizesList;
180 	int facetIndicesArray[];
181 	facetSizesList =
182 	    new Vector(length/6);  // worst case size (every poly one vert)
183 		// Note that our array sizes are hardcoded because we don't
184 		// know until we're done how large they will be
185 	facetIndicesArray = new int[length/2];
186 	ShapeHolder shape = new ShapeHolder(debugPrinter.getValidOutput());
187 	debugOutputLn(VALUES, "new shape = " + shape);
188 	shape.coordsArray = coordsArray;
189 	shape.facetSizesList = facetSizesList;
190 	//shape.facetIndicesList = facetIndicesList;
191 	shape.facetIndicesArray = facetIndicesArray;
192 	shapeList.addElement(shape);
193 
194     //long startTime = (new Date()).getTime();
195 	boolean firstTime = true;
196 	while (lengthRead < length) {
197 	    int numVerts = theReader.getShortInt();
198 	    lengthRead += 2;
199 	    int intArray[] = new int[numVerts];
200 	    for (int i = 0; i < numVerts; ++i) {
201 		intArray[i] = theReader.getShortInt();
202 		lengthRead += 2;
203 	    }
204 
205 	    int numSurf = theReader.getShortInt();
206 	    lengthRead += 2;
207 	    long startTimeBuff = 0, startTimeList = 0;
208 	    if (!firstTime &&
209 		(numSurf != prevNumSurf ||
210 		 ((numVerts != prevNumVerts) &&
211 		  ((prevNumVerts < 3) ||
212 		   (numVerts < 3))))) {
213 		// If above true, then start new shape
214 		shape = getAppropriateShape(numSurf, numVerts);
215 		if (shape == null) {
216 		    //debugOutputLn(LINE_TRACE, "Starting new shape");
217 		    facetSizesList = new Vector(length/6);
218 		    facetIndicesArray = new int[length/2];
219 		    shape = new ShapeHolder(debugPrinter.getValidOutput());
220 		    shape.coordsArray = coordsArray;
221 		    shape.facetSizesList = facetSizesList;
222 		    //shape.facetIndicesList = facetIndicesList;
223 		    shape.facetIndicesArray = facetIndicesArray;
224 		    shape.numSurf = numSurf;
225 		    shape.numVerts = numVerts;
226 		    shapeList.addElement(shape);
227 		    }
228 		else {
229 		    facetSizesList = shape.facetSizesList;
230 		    facetIndicesArray = shape.facetIndicesArray;
231 		}
232 	    }
233 	    else {
234 		shape.numSurf = numSurf;
235 		shape.numVerts = numVerts;
236 	    }
237 	    prevNumVerts = numVerts;
238 	    prevNumSurf = numSurf;
239 	    facetSizesList.addElement(new Integer(numVerts));
240 
241 	    int currPtr = 0;
242 	    System.arraycopy(intArray, 0,
243 			     facetIndicesArray, shape.currentNumIndices,
244 			     numVerts);
245 	    shape.currentNumIndices += numVerts;
246 	    if (numSurf < 0) {   // neg number means detail poly
247 		int numPolys = theReader.getShortInt();
248 		lengthRead += skipDetailPolygons(numPolys);
249 		shape.numSurf = ~shape.numSurf & 0xffff;
250 		if (shape.numSurf == 0)
251 		    shape.numSurf = 1;  // Can't have surface = 0
252 	    }
253 	    firstTime = false;
254 	}
255     }
256 
257 	/**
258 	* Parses file to get the names of all surfaces.  Each polygon will
259 	* be associated with a particular surface number, which is the index
260 	* number of these names
261 	*/
getSrfs(int length)262     void getSrfs(int length) {
263 	String surfName = new String();
264     surfNameList = new Vector(length/2);  // worst case size (each name 2 chars long)
265 	int lengthRead = 0;
266 	int stopMarker = theReader.getMarker() + length;
267 
268         int surfIndex = 0;
269 	while (theReader.getMarker() < stopMarker) {
270 	    debugOutputLn(VALUES, "marker, stop = " +
271 			  theReader.getMarker() + ", " + stopMarker);
272 	    debugOutputLn(LINE_TRACE, "About to call getString");
273 	    surfName = theReader.getString();
274 	    debugOutputLn(VALUES, "Surfname = " + surfName);
275 	    surfNameList.addElement(surfName);
276 	}
277     }
278 
279 	/**
280 	* Parses file to get all vertices
281 	*/
getPnts(int length)282     void getPnts(int length) throws ParsingErrorException {
283 	int numVerts = length / 12;
284 	float x, y, z;
285 
286 	coordsArray = new float[numVerts*3];
287 	theReader.getVerts(coordsArray, numVerts);
288     }
289 
290 	/**
291 	* Creates new LwoSurface object that parses file and gets all
292 	* surface parameters for a particular surface
293 	*/
getSurf(int length)294     void getSurf(int length) throws FileNotFoundException {
295 	debugOutputLn(TRACE, "getSurf()");
296 
297 	// Create LwoSurface object to read and hold each surface, then
298 	// store that surface in a vector of all surfaces.
299 
300 	LwoSurface surf = new LwoSurface(theReader, length,
301 		debugPrinter.getValidOutput());
302 	surfaceList.addElement(surf);
303     }
304 
305 
306     /**
307      * parses entire file.
308      * return -1 on error or 0 on completion
309      */
parseFile()310     int parseFile() throws FileNotFoundException, IncorrectFormatException {
311 	debugOutputLn(TRACE, "parseFile()");
312 	int length = 0;
313 	int lengthRead = 0;
314 	int fileLength = 100000;
315 
316 	long loopStartTime = System.currentTimeMillis();
317 	// Every parsing unit begins with a four character string
318 	String tokenString = theReader.getToken();
319 
320 	while (!(tokenString == null) &&
321 		  lengthRead < fileLength) {
322 	    long startTime = System.currentTimeMillis();
323 	    // Based on value of tokenString, go to correct parsing method
324 	    length = theReader.getInt();
325 
326 	    lengthRead += 4;
327 	    //debugOutputLn(VALUES, "length, lengthRead, fileLength = " +
328 	    //	      length + ", " + lengthRead + ", " + fileLength);
329 	    //debugOutputLn(VALUES, "LWOB marker is at: " + theReader.getMarker());
330 
331 	    if (tokenString.equals("FORM")) {
332 		//debugOutputLn(LINE_TRACE, "got a form");
333 		fileLength = length + 4;
334 		length = 0;
335 		tokenString = theReader.getToken();
336 		lengthRead += 4;
337 		if (!tokenString.equals("LWOB"))
338 		    throw new IncorrectFormatException(
339 			"File not of FORM-length-LWOB format");
340 	    }
341 	    else if (tokenString.equals("PNTS")) {
342 		//debugOutputLn(LINE_TRACE, "PNTS");
343 		getPnts(length);
344 		debugOutputLn(TIME, "done with " + tokenString + " in " +
345 			      (System.currentTimeMillis() - startTime));
346 	    }
347 	    else if (tokenString.equals("POLS")) {
348 		//debugOutputLn(LINE_TRACE, "POLS");
349 		getPols(length);
350 		debugOutputLn(TIME, "done with " + tokenString + " in " +
351 			      (System.currentTimeMillis() - startTime));
352 	    }
353 	    else if (tokenString.equals("SRFS")) {
354 		//debugOutputLn(LINE_TRACE, "SRFS");
355 		getSrfs(length);
356 		    debugOutputLn(TIME, "done with " + tokenString + " in " +
357 		    (System.currentTimeMillis() - startTime));
358 	    }
359 	    else if (tokenString.equals("CRVS")) {
360 		//debugOutputLn(LINE_TRACE, "CRVS");
361 		theReader.skipLength(length);
362 		    //debugOutputLn(TIME, "done with " + tokenString + " in " +
363 		    //	(System.currentTimeMillis() - startTime));
364 	    }
365 	    else if (tokenString.equals("PCHS")) {
366 		//debugOutputLn(LINE_TRACE, "PCHS");
367 		theReader.skipLength(length);
368 		    //debugOutputLn(TIME, "done with " + tokenString + " in " +
369 		    //	(System.currentTimeMillis() - startTime));
370 	    }
371 	    else if (tokenString.equals("SURF")) {
372 		//debugOutputLn(LINE_TRACE, "SURF");
373 		getSurf(length);
374 		//debugOutputLn(VALUES, "Done with SURF, marker = " + theReader.getMarker());
375 		debugOutputLn(TIME, "done with " + tokenString + " in " +
376 			      (System.currentTimeMillis() - startTime));
377 	    }
378 	    else if (tokenString.equals("LWOB")) {
379 		//debugOutputLn(LINE_TRACE, "LWOB");
380 	    }
381 	    else {
382 		//debugOutputLn(LINE_TRACE, "Unknown object = " + tokenString);
383 		theReader.skipLength(length);
384 		    //debugOutputLn(TIME, "done with " + tokenString + " in " +
385 		    //	(System.currentTimeMillis() - startTime));
386 	    }
387 	    lengthRead += length;
388 	    if (lengthRead < fileLength) {
389 		//debugOutputLn(VALUES, "end of parseFile, length, lengthRead = " +
390 		    //	  length + ", " + lengthRead);
391 		tokenString = theReader.getToken();
392 		lengthRead += 4;
393 		//debugOutputLn(VALUES, "just got tokenString = " + tokenString);
394 	    }
395 	}
396 	debugOutputLn(TIME, "done with parseFile in " +
397 		      (System.currentTimeMillis() - loopStartTime));
398 	return 0;
399     }
400 
401 	/**
402 	* This method is used only for testing
403 	*/
main(String[] args)404     static void main(String[] args) {
405 	String fileName;
406 	if (args.length == 0)
407 	    fileName = "cube.obj";
408 	else
409 	    fileName = args[0];
410 
411         try {
412 	  LwoParser theParser = new LwoParser(fileName, 0);
413 	}
414 	catch (FileNotFoundException e) {
415 	    System.err.println(e.getMessage());
416 	    e.printStackTrace();
417 	    System.exit(1);
418 	}
419     }
420 }
421 
422 
423 
424 
425