summaryrefslogtreecommitdiffstats
path: root/src/demos/util/ObjReader.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/demos/util/ObjReader.java')
-rw-r--r--src/demos/util/ObjReader.java368
1 files changed, 368 insertions, 0 deletions
diff --git a/src/demos/util/ObjReader.java b/src/demos/util/ObjReader.java
new file mode 100644
index 0000000..75a2d08
--- /dev/null
+++ b/src/demos/util/ObjReader.java
@@ -0,0 +1,368 @@
+/*
+ * Copyright (c) 2003 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 or intended for use
+ * in the design, construction, operation or maintenance of any nuclear
+ * facility.
+ *
+ * Sun gratefully acknowledges that this software was originally authored
+ * and developed by Kenneth Bradley Russell and Christopher John Kline.
+ */
+
+package demos.util;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.nio.FloatBuffer;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.StringTokenizer;
+import com.sun.opengl.util.BufferUtil;
+
+
+
+/** Simple parser for Wavefront .OBJ files. Does not support all file
+ options -- currently requires vertices and normals (only) to be
+ present. */
+
+public class ObjReader {
+ private int verticesPerFace = -1;
+ private FloatBuffer vertices;
+ private FloatBuffer normals;
+ private float[] aabbMin = new float[3];
+ private float[] aabbMax = new float[3];
+ private float[] center = new float[3];
+ private float radius;
+ // If we wanted this to be really general we'd have an array of
+ // FloatLists for the various kinds of vertices as well
+ private FloatList tmpVertices;
+ private FloatList tmpVertexNormals;
+ private IntList faceIndices;
+ private IntList[] tmpFaceIndices;
+
+ public ObjReader(String filename) throws IOException {
+ this(new File(filename));
+ }
+
+ public ObjReader(InputStream in) throws IOException {
+ this(new InputStreamReader(in));
+ }
+
+ public ObjReader(File file) throws IOException {
+ this (new FileReader(file));
+ }
+
+ public ObjReader(Reader r) throws IOException {
+ BufferedReader reader = new BufferedReader(r);
+ String line = null;
+ int lineNo = 0;
+ float[] floatTmp = new float[3];
+
+ while ((line = reader.readLine()) != null) {
+ ++lineNo;
+ if (line.length() > 0) {
+ char c = line.charAt(0);
+ // FIXME: support continuation of lines with trailing '\'
+ switch (c) {
+ case '#':
+ break;
+
+ case 'v':
+ if (Character.isWhitespace(line.charAt(1))) {
+ addVertex(parseFloats(line, 3, floatTmp, lineNo));
+ } else if (line.startsWith("vn")) {
+ addVertexNormal(parseFloats(line, 3, floatTmp, lineNo));
+ } else {
+ throw new IOException("Unsupported vertex command on line " + lineNo);
+ }
+ break;
+
+ case 'f':
+ parseIndices(line, lineNo);
+
+ default:
+ // For now we ignore all other lines
+ }
+ }
+ }
+
+ // Now have all vertex information.
+ // Make it possible to use same indices for both vertices and normals
+ condenseIndices();
+
+ // Compute axis-aligned bounding box and radius
+ computeBoundingBox();
+ }
+
+ public void rescale(float amount) {
+ for (int i = 0; i < vertices.capacity(); i++) {
+ vertices.put(i, vertices.get(i) * amount);
+ }
+ }
+
+ public FloatBuffer getVertices() {
+ return vertices;
+ }
+
+ public FloatBuffer getVertexNormals() {
+ return normals;
+ }
+
+ public int[] getFaceIndices() {
+ return faceIndices.getData();
+ }
+
+ public int getVerticesPerFace() {
+ return verticesPerFace;
+ }
+
+ public float[] getAABBMin() {
+ return aabbMin;
+ }
+
+ public float[] getAABBMax() {
+ return aabbMax;
+ }
+
+ public float[] getCenter() {
+ return center;
+ }
+
+ public float getRadius() {
+ return radius;
+ }
+
+ //----------------------------------------------------------------------
+ // Internals only below this point
+ //
+
+ private void addVertex(float[] tmp) {
+ if (tmpVertices == null) {
+ tmpVertices = new FloatList();
+ }
+ for (int i = 0; i < 3; i++) {
+ tmpVertices.add(tmp[i]);
+ }
+ }
+
+ private void addVertexNormal(float[] tmp) {
+ if (tmpVertexNormals == null) {
+ tmpVertexNormals = new FloatList();
+ }
+ for (int i = 0; i < 3; i++) {
+ tmpVertexNormals.add(tmp[i]);
+ }
+ }
+
+ private float[] parseFloats(String line, int num, float[] tmp, int lineNo) throws IOException {
+ StringTokenizer tok = new StringTokenizer(line);
+ tok.nextToken(); // skip command
+ int idx = 0;
+ while (tok.hasMoreTokens()) {
+ if (idx >= tmp.length) {
+ throw new IOException("Too many floating-point values on line " + lineNo);
+ }
+ tmp[idx++] = Float.parseFloat(tok.nextToken());
+ }
+ return tmp;
+ }
+
+ private void parseIndices(String line, int lineNo) throws IOException {
+ StringTokenizer tok = new StringTokenizer(line);
+ tok.nextToken(); // skip command
+ List tokens = new ArrayList();
+ while (tok.hasMoreTokens()) {
+ tokens.add(tok.nextToken());
+ }
+ // This is the number of vertices in this face.
+ // If we seem to have already found this, it had better match the
+ // previously read value (for now - don't want to add the
+ // complexity of supporting some faces with a certain number of
+ // vertices and some with a different number)
+ if (verticesPerFace < 0) {
+ verticesPerFace = tokens.size();
+ } else {
+ if (verticesPerFace != tokens.size()) {
+ throw new IOException("Face on line " + lineNo + " had " + tokens.size() +
+ " vertices, but had already previously set the number of vertices per face to " +
+ verticesPerFace);
+ }
+ }
+ // Now read the individual indices out of each token
+ for (Iterator iter = tokens.iterator(); iter.hasNext(); ) {
+ String indices = (String) iter.next();
+ if (tmpFaceIndices == null) {
+ StringTokenizer tmpTok = new StringTokenizer(indices, "/");
+ int numIndicesPerVertex = 0;
+ while (tmpTok.hasMoreTokens()) {
+ tmpTok.nextToken();
+ ++numIndicesPerVertex;
+ }
+ tmpFaceIndices = new IntList[numIndicesPerVertex];
+ for (int i = 0; i < numIndicesPerVertex; i++) {
+ tmpFaceIndices[i] = new IntList();
+ }
+ }
+
+ StringTokenizer tok2 = new StringTokenizer(indices, "/");
+ int which = 0;
+ while (tok2.hasMoreTokens()) {
+ if (which >= tmpFaceIndices.length) {
+ throw new IOException("Expected all vertices to have " + tmpFaceIndices.length +
+ " indices based on earlier input, but saw vertex with more on line " + lineNo);
+ }
+ String token = tok2.nextToken();
+ int index = Integer.parseInt(token);
+ tmpFaceIndices[which].add(index);
+ ++which;
+ }
+ }
+ }
+
+ // Don't know the hashing rules for arrays off the top of my head
+ static class Indices {
+ int[] data;
+ Indices(int[] data) {
+ this.data = data;
+ }
+
+ public boolean equals(Object obj) {
+ if ((obj == null) || (!(obj instanceof Indices))) {
+ return false;
+ }
+
+ Indices other = (Indices) obj;
+
+ if (data.length != other.data.length) {
+ return false;
+ }
+
+ for (int i = 0; i < data.length; i++) {
+ if (data[i] != other.data[i]) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public int hashCode() {
+ int hash = 0;
+ for (int i = 0; i < data.length; i++) {
+ hash ^= data[i];
+ }
+ return hash;
+ }
+ }
+
+ private void condenseIndices() {
+ FloatList newVertices = new FloatList();
+ FloatList newVertexNormals = new FloatList();
+ IntList newIndices = new IntList();
+ int nextIndex = 0;
+ HashMap condensingMap = new HashMap();
+ for (int i = 0; i < tmpFaceIndices[0].size(); i++) {
+ Indices indices = getIndices(i);
+ Integer newIndex = (Integer) condensingMap.get(indices);
+ if (newIndex == null) {
+ // Fabricate new vertex and normal index for this one
+ // FIXME: generalize this by putting vertices and vertex
+ // normals in FloatList[] as well
+ condensingMap.put(indices, new Integer(nextIndex));
+ int vtxIdx = 3 * (indices.data[0] - 1);
+ int vtxNrmIdx = 3 * (indices.data[1] - 1);
+ newVertices.add(tmpVertices.get(vtxIdx + 0));
+ newVertices.add(tmpVertices.get(vtxIdx + 1));
+ newVertices.add(tmpVertices.get(vtxIdx + 2));
+ newVertexNormals.add(tmpVertexNormals.get(vtxNrmIdx + 0));
+ newVertexNormals.add(tmpVertexNormals.get(vtxNrmIdx + 1));
+ newVertexNormals.add(tmpVertexNormals.get(vtxNrmIdx + 2));
+ newIndices.add(nextIndex);
+ ++nextIndex;
+ } else {
+ newIndices.add(newIndex.intValue());
+ }
+ }
+ newVertices.trim();
+ newVertexNormals.trim();
+ newIndices.trim();
+ vertices = BufferUtil.newFloatBuffer(newVertices.size());
+ vertices.put(newVertices.getData());
+ vertices.rewind();
+ normals = BufferUtil.newFloatBuffer(newVertexNormals.size());
+ normals.put(newVertexNormals.getData());
+ normals.rewind();
+ faceIndices = newIndices;
+ tmpVertices = null;
+ tmpVertexNormals = null;
+ }
+
+ private void computeBoundingBox() {
+ for (int i = 0; i < vertices.capacity(); i += 3) {
+ if (i == 0) {
+ aabbMin[0] = vertices.get(i + 0);
+ aabbMin[1] = vertices.get(i + 1);
+ aabbMin[2] = vertices.get(i + 2);
+ aabbMax[0] = vertices.get(i + 0);
+ aabbMax[1] = vertices.get(i + 1);
+ aabbMax[2] = vertices.get(i + 2);
+ } else {
+ aabbMin[0] = Math.min(aabbMin[0], vertices.get(i + 0));
+ aabbMin[1] = Math.min(aabbMin[1], vertices.get(i + 1));
+ aabbMin[2] = Math.min(aabbMin[2], vertices.get(i + 2));
+ aabbMax[0] = Math.max(aabbMax[0], vertices.get(i + 0));
+ aabbMax[1] = Math.max(aabbMax[1], vertices.get(i + 1));
+ aabbMax[2] = Math.max(aabbMax[2], vertices.get(i + 2));
+ }
+ }
+ center[0] = 0.5f * (aabbMin[0] + aabbMax[0]);
+ center[1] = 0.5f * (aabbMin[1] + aabbMax[1]);
+ center[2] = 0.5f * (aabbMin[2] + aabbMax[2]);
+ radius = (float) Math.sqrt((aabbMax[0] - center[0]) * (aabbMax[0] - center[0]) +
+ (aabbMax[1] - center[1]) * (aabbMax[1] - center[1]) +
+ (aabbMax[2] - center[2]) * (aabbMax[2] - center[2]));
+ }
+
+ private Indices getIndices(int index) {
+ int[] indices = new int[tmpFaceIndices.length];
+ for (int i = 0; i < tmpFaceIndices.length; i++) {
+ indices[i] = tmpFaceIndices[i].get(index);
+ }
+ return new Indices(indices);
+ }
+}