From 30fadee2f591d5b80558ef9defd2ec271ad3af5a Mon Sep 17 00:00:00 2001 From: rodgersgb Date: Tue, 29 Apr 2008 19:49:13 +0000 Subject: Added a folder remotely git-svn-id: file:///usr/local/projects/SUN/JOGL/git-svn/svn-server-sync/joglutils/trunk@87 83d24430-9974-4f80-8418-2cc3294053b9 --- .../java/joglutils/model/loader/LoaderFactory.java | 62 ++ .../java/joglutils/model/loader/MaxConstants.java | 109 +++ src/net/java/joglutils/model/loader/MaxLoader.java | 876 +++++++++++++++++++++ .../joglutils/model/loader/WaveFrontLoader.java | 452 +++++++++++ src/net/java/joglutils/model/loader/iLoader.java | 21 + 5 files changed, 1520 insertions(+) create mode 100644 src/net/java/joglutils/model/loader/LoaderFactory.java create mode 100644 src/net/java/joglutils/model/loader/MaxConstants.java create mode 100644 src/net/java/joglutils/model/loader/MaxLoader.java create mode 100644 src/net/java/joglutils/model/loader/WaveFrontLoader.java create mode 100644 src/net/java/joglutils/model/loader/iLoader.java diff --git a/src/net/java/joglutils/model/loader/LoaderFactory.java b/src/net/java/joglutils/model/loader/LoaderFactory.java new file mode 100644 index 0000000..59a680f --- /dev/null +++ b/src/net/java/joglutils/model/loader/LoaderFactory.java @@ -0,0 +1,62 @@ +/* + * LoaderFactory.java + * + * Created on February 27, 2008, 10:35 PM + * + * To change this template, choose Tools | Template Manager + * and open the template in the editor. + */ + +package net.java.joglutils.model.loader; + +import net.java.joglutils.model.ModelLoadException; +import net.java.joglutils.model.geometry.Model; + +/** + * + * @author Brian Wood + */ +public class LoaderFactory { + private static final int FILETYPE_UNKNOWN = -1; + private static final int FILETYPE_3DS = 1; + private static final int FILETYPE_OBJ = 2; + + public static Model load(String source) throws ModelLoadException { + iLoader loader = getLoader(source); + if (loader == null) + return null; + + return loader.load(source); + } + + private static iLoader getLoader(String path) { + switch(determineFiletype(path)) { + case FILETYPE_3DS: + return new MaxLoader(); + + case FILETYPE_OBJ: + return new WaveFrontLoader(); + + default: + return null; + } + } + + /** + * Parses the file suffix to determine what file format the model is in. + * + * @param path File path info + * @returns int Constant indicating file type + */ + private static int determineFiletype(String path) { + int type = FILETYPE_UNKNOWN; + String tokens[] = path.split("\\."); + + if(tokens[tokens.length - 1].equals("3ds")) + type = FILETYPE_3DS; + else if(tokens[tokens.length - 1].equals("obj")) + type = FILETYPE_OBJ; + + return type; + } +} diff --git a/src/net/java/joglutils/model/loader/MaxConstants.java b/src/net/java/joglutils/model/loader/MaxConstants.java new file mode 100644 index 0000000..57b0b7c --- /dev/null +++ b/src/net/java/joglutils/model/loader/MaxConstants.java @@ -0,0 +1,109 @@ +/* + * MaxConstants.java + * + * Created on February 12, 2008, 10:20 PM + * + * To change this template, choose Tools | Template Manager + * and open the template in the editor. + */ + +package net.java.joglutils.model.loader; + +/** + * Constants representing block types in the 3DS file. This is not a complete + * list; it only includes the types that are currently recognized. They are + * all of type int since 3DS files allocate 16 bits to the + * block type field. + * + * @author RodgersGB + * @version $Revision: 1.0 $ + */ +interface MaxConstants { + // 0xMISC Administrative codes + public static final int TYPE_3DS_FILE = 0x4D4D; + public static final int TYPE_3DS_VERSION = 0x0002; + public static final int TYPE_MESH_DATA = 0x3D3D; + public static final int TYPE_MESH_VERSION = 0x3D3E; + + // 0x0--- Data type codes + public static final int TYPE_COLOR_I = 0x0011; + public static final int TYPE_COLOR_F = 0x0010; + public static final int TYPE_COLOR_LIN_I = 0x0012; + public static final int TYPE_COLOR_LIN_F = 0x0013; + public static final int TYPE_PERCENT_I = 0x0030; + public static final int TYPE_PERCENT_F = 0x0031; + + // 0x1--- Background codes + public static final int TYPE_BG_BITMAP = 0x1100; + public static final int TYPE_BG_USE_BITMAP = 0x1101; + public static final int TYPE_BACKGROUND_COLOR = 0x1200; + public static final int TYPE_BG_USE_SOLID = 0x1201; + public static final int TYPE_BG_GRADIENT = 0x1300; + public static final int TYPE_BG_USE_GRADIENT = 0x1301; + + // 0x2--- Ambient light/fog code + public static final int TYPE_AMBIENT_COLOR = 0x2100; + public static final int TYPE_FOG = 0x2200; + public static final int TYPE_USE_FOG = 0x2201; + public static final int TYPE_FOG_BGND = 0x2210; + public static final int TYPE_LAYER_FOG = 0x2302; + public static final int TYPE_USE_LAYER_FOG = 0x2302; + + // 0x3--- View codes + + // 0x4--- Object data codes + public static final int TYPE_NAMED_OBJECT = 0x4000; + public static final int TYPE_TRIANGLE_OBJECT = 0x4100; + public static final int TYPE_POINT_LIST = 0x4110; + public static final int TYPE_VERTEX_OPTIONS = 0x4111; + public static final int TYPE_FACE_LIST = 0x4120; + public static final int TYPE_MAT_FACE_LIST = 0x4130; + public static final int TYPE_MAT_UV = 0x4140; + public static final int TYPE_SMOOTH_GROUP = 0x4150; + public static final int TYPE_MESH_MATRIX = 0x4160; + public static final int TYPE_MESH_COLOR = 0x4165; + // 0x46-- Light data codes + public static final int TYPE_DIRECT_LIGHT = 0x4600; + public static final int TYPE_SPOTLIGHT = 0x4610; + public static final int TYPE_ATTENUATION = 0x4625; + public static final int TYPE_AMBIENT_LIGHT = 0x4680; + // 0x47-- Camera data codes + public static final int TYPE_CAMERA = 0x4700; + + // 0x5--- Unknown shape stuff + + // 0x6--- Path/curve codes + + // 0x7--- Viewport codes + + // 0x8--- Unknown XDATA codes + + // 0xA--- Material codes + public static final int TYPE_MATERIAL = 0xAFFF; + public static final int TYPE_MATERIAL_NAME = 0xA000; + public static final int TYPE_MAT_AMBIENT = 0xA010; + public static final int TYPE_MAT_DIFFUSE = 0xA020; + public static final int TYPE_MAT_SPECULAR = 0xA030; + public static final int TYPE_MAT_SHININESS = 0xA040; + public static final int TYPE_MAT_SHININESS2 = 0xA041; + public static final int TYPE_MAT_TRANSPARENCY = 0xA050; + public static final int TYPE_MAT_XPFALL = 0xA052; + public static final int TYPE_MAT_REFBLUR = 0xA053; + public static final int TYPE_MAT_2_SIDED = 0xA081; + public static final int TYPE_MAT_SELF_ILPCT = 0xA084; + public static final int TYPE_MAT_SHADING = 0xA100; + // 0xA2+- Texture codes + public static final int TYPE_MAT_TEXMAP = 0xA200; + public static final int TYPE_MAT_MAPNAME = 0xA300; + + // 0xB--- Node codes + public static final int TYPE_KEY_FRAME = 0xB000; + public static final int TYPE_FRAME_INFO = 0xB002; + public static final int TYPE_FRAMES = 0xB008; + public static final int TYPE_PIVOT_POINT = 0xB020; + + + // 0xC--- Unknown codes + // 0xD--- Unknown codes + // 0xF--- Unknown codes +} \ No newline at end of file diff --git a/src/net/java/joglutils/model/loader/MaxLoader.java b/src/net/java/joglutils/model/loader/MaxLoader.java new file mode 100644 index 0000000..d71baeb --- /dev/null +++ b/src/net/java/joglutils/model/loader/MaxLoader.java @@ -0,0 +1,876 @@ +/* + * Copyright (c) 2006 Greg Rodgers 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. + * + * The names of Greg Rodgers, Sun Microsystems, Inc. or the names of + * contributors may not 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. GREG , + * SUN MICROSYSTEMS, INC. ("SUN"), AND SUN'S 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 GREG + * RODGERS, SUN, OR SUN'S 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 GREG + * RODGERS OR 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. + */ + +package net.java.joglutils.model.loader; + +import java.awt.Color; +import java.io.DataInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import net.java.joglutils.model.ResourceRetriever; +import net.java.joglutils.model.geometry.Bounds; +import net.java.joglutils.model.geometry.Face; +import net.java.joglutils.model.geometry.Material; +import net.java.joglutils.model.geometry.Mesh; +import net.java.joglutils.model.geometry.Model; +import net.java.joglutils.model.geometry.TexCoord; +import net.java.joglutils.model.geometry.Vec4; + +public class MaxLoader implements MaxConstants, iLoader { +// File reader + private File file; + private boolean loaded = false; + private DataInputStream dataInputStream; + +// Global chunks + private Chunk currentChunk, tempChunk; + + /** Bounds of the model */ + private Bounds bounds = new Bounds(); + + /** Center of the model */ + private Vec4 center = new Vec4(0.0f, 0.0f, 0.0f); + + // Constructor + public MaxLoader() + { + currentChunk = new Chunk(); + tempChunk = new Chunk(); + } + + public Model load(String source) { + Model model = new Model(source); + load(model); + return model; + } + // Verified + public boolean load(Model model) + { + try { + InputStream stream = ResourceRetriever.getResourceAsInputStream(model.getSource()); + if (stream == null) { + System.out.println("stream is null"); + return false; + } + + dataInputStream = new DataInputStream(stream); + if (dataInputStream == null) { + System.out.println("dataInputStream is null"); + return false; + } + + readChunkHeader(currentChunk); + + } catch(IOException e) { + System.out.println("IOException e:" + e); + return false; + } + + + if (currentChunk.id != TYPE_3DS_FILE) { + System.err.println("Unable to load PRIMARY chuck from file!"); + return false; + } + + processNextChunk(model, currentChunk); + computeNormals(model); + + try { + dataInputStream.close(); + + } catch (IOException e) { + System.err.println("Error: File IO error in: Closing File"); + return false; + } + + loaded = true; + + model.setBounds(this.bounds); + model.setCenterPoint(this.center); + + return loaded; + } + + /** + * Read the stream completely + * + * @param buffer + * @param offset + * @param length + * @throws IOException + */ + private int readCompletely(byte buffer[], int offset, int length) throws IOException { + dataInputStream.readFully(buffer, offset, length); + return length; + } + + // Verified + void processNextChunk(Model model, Chunk previousChunk) + { + int version = 0; + byte buffer[] = null; + + currentChunk = new Chunk(); + + try { + while (previousChunk.bytesRead < previousChunk.length) { + readChunkHeader(currentChunk); + + switch (currentChunk.id) { + case TYPE_3DS_VERSION: + version = readInt(currentChunk); + + if (version > 0x03) + System.err.println("This 3DS file is over version 3 so it may load incorrectly"); + break; + + case TYPE_MESH_DATA: + readChunkHeader(tempChunk); + buffer = new byte[tempChunk.length - tempChunk.bytesRead]; + tempChunk.bytesRead += readCompletely(buffer, 0, buffer.length); + // TODO:DELETE tempChunk.bytesRead += dataInputStream.read(buffer, 0, tempChunk.length - tempChunk.bytesRead); + currentChunk.bytesRead += tempChunk.bytesRead; + processNextChunk(model, currentChunk); + break; + + case TYPE_MATERIAL: + Material mat = new Material(); + model.addMaterial(mat); + processNextMaterialChunk(model, mat, currentChunk); + break; + + case TYPE_NAMED_OBJECT: + Mesh obj = new Mesh(); + obj.name = readString(currentChunk); + model.addMesh(obj); + processNextObjectChunk(model, obj, currentChunk); + break; + + case TYPE_KEY_FRAME: + processKeyFrame(currentChunk); + break; + + default: + buffer = new byte[currentChunk.length - currentChunk.bytesRead]; + currentChunk.bytesRead += readCompletely(buffer, 0, buffer.length); + break; + } + previousChunk.bytesRead += currentChunk.bytesRead; + } + } + catch (IOException e) { + System.err.println("Error: File IO error in: Process Next Chunk"); + return; + } + currentChunk = previousChunk; + } + + private void processKeyFrame(Chunk root) throws IOException { + currentChunk = new Chunk(); + byte buffer[] = null; + + while (root.bytesRead < root.length) { + readChunkHeader(currentChunk); + + switch (currentChunk.id) { + default: + buffer = new byte[currentChunk.length - currentChunk.bytesRead]; + currentChunk.bytesRead += readCompletely(buffer, 0, buffer.length); + break; + } + root.bytesRead += currentChunk.bytesRead; + } + currentChunk = root; + } + + // Verified + private void readChunkHeader(Chunk chunk) throws IOException + { + chunk.bytesRead = 0; + chunk.id = this.readShort(chunk); + chunk.length = this.readInt(chunk); + } + + // Verified + private void processNextObjectChunk(Model model, Mesh object, Chunk previousChunk) + { + byte buffer[] = null; + int bytesread; + + currentChunk = new Chunk(); + + try { + while (previousChunk.bytesRead < previousChunk.length) { + readChunkHeader(currentChunk); + + switch (currentChunk.id) { + case TYPE_TRIANGLE_OBJECT: + processNextObjectChunk(model, object, currentChunk); + break; + + case TYPE_DIRECT_LIGHT: + buffer = new byte[currentChunk.length - currentChunk.bytesRead]; + bytesread = readCompletely(buffer, 0, buffer.length); + currentChunk.bytesRead += bytesread; + break; + + case TYPE_POINT_LIST: + readVertices(object, currentChunk); + break; + + case TYPE_FACE_LIST: + readFaceList(object, currentChunk); + break; + + case TYPE_MAT_FACE_LIST: + readObjectMaterial(model, object, currentChunk); + break; + + case TYPE_MAT_UV: + readUVCoordinates(object, currentChunk); + break; + + default: + buffer = new byte[currentChunk.length - currentChunk.bytesRead]; + bytesread = readCompletely(buffer, 0, buffer.length); + // TODO:DELETE currentChunk.bytesRead += dataInputStream.read(buffer, 0, currentChunk.length - currentChunk.bytesRead); + currentChunk.bytesRead += bytesread; + break; + } + previousChunk.bytesRead += currentChunk.bytesRead; + } + } + catch (IOException e) { + System.err.println("Error: File IO error in: Process Next Object Chunk"); + return; + } + + currentChunk = previousChunk; + } + + // Verified + private void processNextMaterialChunk(Model model, Material material, Chunk previousChunk) + { + byte buffer[] = null; + + currentChunk = new Chunk(); + + try { + while (previousChunk.bytesRead < previousChunk.length) { + readChunkHeader(currentChunk); + + switch (currentChunk.id) + { + case TYPE_MATERIAL_NAME: + material.strName = readString(currentChunk); + buffer = new byte[currentChunk.length - currentChunk.bytesRead]; + currentChunk.bytesRead += readCompletely(buffer, 0, buffer.length); + break; + + case TYPE_MAT_AMBIENT: + material.ambientColor = readColor(currentChunk); + break; + + case TYPE_MAT_DIFFUSE: + material.diffuseColor = readColor(currentChunk); + break; + + case TYPE_MAT_SPECULAR: + material.specularColor = readColor(currentChunk); + break; + + case TYPE_MAT_SHININESS: + material.shininess = 1.0f+127.0f*readPercentage(currentChunk); + break; + + case TYPE_MAT_SHININESS2: + material.shininess2 = 1.0f+127.0f*readPercentage(currentChunk); + break; + + case TYPE_MAT_TRANSPARENCY: + material.transparency = readPercentage(currentChunk); + break; + + case TYPE_MAT_2_SIDED: + buffer = new byte[currentChunk.length - currentChunk.bytesRead]; + currentChunk.bytesRead += readCompletely(buffer, 0, buffer.length); + break; + + case TYPE_MAT_XPFALL: + float xpf = readPercentage(currentChunk); + break; + + case TYPE_MAT_REFBLUR: + float ref = readPercentage(currentChunk); + break; + + case TYPE_MAT_SELF_ILPCT: + float il = readPercentage(currentChunk); + break; + + case TYPE_MAT_SHADING: + int shading = readShort(currentChunk); + // Some kind of code for the rendering type: 1, 3, 4, etc. + break; + + case TYPE_MAT_TEXMAP: + processNextMaterialChunk(model, material, currentChunk); + break; + + case TYPE_MAT_MAPNAME: + material.strFile = readString(currentChunk); + buffer = new byte[currentChunk.length - currentChunk.bytesRead]; + currentChunk.bytesRead += readCompletely(buffer, 0, buffer.length); + break; + + default: + buffer = new byte[currentChunk.length - currentChunk.bytesRead]; + currentChunk.bytesRead += readCompletely(buffer, 0, buffer.length); + break; + } + + previousChunk.bytesRead += currentChunk.bytesRead; + } + } catch (IOException e) { + System.err.println("Error: File IO error in: Process Next Material Chunk"); + return; + } + currentChunk = previousChunk; + } + +// // Verified +// private void readObjectMaterial(Model model, Mesh object, Chunk previousChunk) throws IOException +// { +// String strMaterial = new String(); +// byte buffer[] = null; +// +// strMaterial = readString(previousChunk); +// +// for (int i=0; i> 8) & 0xff; +// +// return (short) (b1 << 8 | b2 << 0); +// } +// +// private static int getNextByte(DataInputStream stream) { +// try { +// return stream.read() & 0xff; +// } catch (Exception e) { +// return 0; +// } +// } +// +// private static int myReadShort(DataInputStream stream) { +// int b1 = getNextByte(stream); +// int b2 = getNextByte(stream) << 8; +// +// return b1 | b2; +// } +// +// private static int swap(int value) +// { +// int b1 = (value >> 0) & 0xff; +// int b2 = (value >> 8) & 0xff; +// int b3 = (value >> 16) & 0xff; +// int b4 = (value >> 24) & 0xff; +// +// return b1 << 24 | b2 << 16 | b3 << 8 | b4 << 0; +// } +// +// private static long swap(long value) +// { +// long b1 = (value >> 0) & 0xff; +// long b2 = (value >> 8) & 0xff; +// long b3 = (value >> 16) & 0xff; +// long b4 = (value >> 24) & 0xff; +// long b5 = (value >> 32) & 0xff; +// long b6 = (value >> 40) & 0xff; +// long b7 = (value >> 48) & 0xff; +// long b8 = (value >> 56) & 0xff; +// +// return b1 << 56 | b2 << 48 | b3 << 40 | b4 << 32 | +// b5 << 24 | b6 << 16 | b7 << 8 | b8 << 0; +// } +// +// private static float swap(float value) +// { +// int intValue = Float.floatToIntBits(value); +// intValue = swap(intValue); +// return Float.intBitsToFloat(intValue); +// } +// +// private static float mySwap(int value) { +// int intValue = swap(value); +// return Float.intBitsToFloat(intValue); +// } +// +// private static double swap(double value) +// { +// long longValue = Double.doubleToLongBits(value); +// longValue = swap(longValue); +// return Double.longBitsToDouble(longValue); +// } + + private class Chunk { + public int id = 0; + public int length = 0; + public int bytesRead = 0; + } +} diff --git a/src/net/java/joglutils/model/loader/WaveFrontLoader.java b/src/net/java/joglutils/model/loader/WaveFrontLoader.java new file mode 100644 index 0000000..f3eeb6b --- /dev/null +++ b/src/net/java/joglutils/model/loader/WaveFrontLoader.java @@ -0,0 +1,452 @@ +/* + * myWaveFrontLoader.java + * + * Created on March 16, 2008, 8:57 PM + * + * To change this template, choose Tools | Template Manager + * and open the template in the editor. + */ + +package net.java.joglutils.model.loader; + +import java.awt.Color; +import java.io.BufferedReader; +import java.io.DataInputStream; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.Vector; +import net.java.joglutils.model.ModelLoadException; +import net.java.joglutils.model.ResourceRetriever; +import net.java.joglutils.model.geometry.Bounds; +import net.java.joglutils.model.geometry.Face; +import net.java.joglutils.model.geometry.Material; +import net.java.joglutils.model.geometry.Mesh; +import net.java.joglutils.model.geometry.Model; +import net.java.joglutils.model.geometry.TexCoord; +import net.java.joglutils.model.geometry.Vec4; + +/** + * + * @author RodgersGB + */ +public class WaveFrontLoader implements iLoader { + public static final String VERTEX_DATA = "v "; + public static final String NORMAL_DATA = "vn "; + public static final String TEXTURE_DATA = "vt "; + public static final String FACE_DATA = "f "; + public static final String SMOOTHING_GROUP = "s "; + public static final String GROUP = "g "; + public static final String OBJECT = "o "; + public static final String COMMENT = "#"; + public static final String EMPTY = ""; + + int vertexTotal = 0; + int textureTotal = 0; + int normalTotal = 0; + + private DataInputStream dataInputStream; + // the model + private Model model = null; + /** Bounds of the model */ + private Bounds bounds = new Bounds(); + /** Center of the model */ + private Vec4 center = new Vec4(0.0f, 0.0f, 0.0f); + private String baseDir = null; + + /** Creates a new instance of myWaveFrontLoader */ + public WaveFrontLoader() { + } + + int numComments = 0; + public Model load(String path) throws ModelLoadException { + model = new Model(path); + Mesh mesh = null; + + baseDir = ""; + String tokens[] = path.split("/"); + for(int i = 0; i < tokens.length - 1; i++) { + baseDir += tokens[i] + "/"; + } + + InputStream stream = null; + try { + stream = ResourceRetriever.getResourceAsInputStream(model.getSource()); + if (stream == null) { + throw new ModelLoadException("Stream is null"); + } + } catch(IOException e) { + throw new ModelLoadException("Caught IO exception: " + e); + } + + try { + // Open a file handle and read the models data + BufferedReader br = new BufferedReader(new InputStreamReader(stream)); + String line = null; + while((line = br.readLine()) != null) { + if (lineIs(COMMENT, line)) { + // ignore comments + numComments++; + continue; + } + + if (line.isEmpty()) { + // igonore empty lines + continue; + } + + if (lineIs(GROUP, line)) { + if (mesh == null) { + mesh = new Mesh(); + } + + mesh.name = parseName(line); + } + + if (lineIs(OBJECT, line)) { + + } + + if (lineIs(VERTEX_DATA, line)) { + if (mesh == null) + mesh = new Mesh(); + + mesh.vertices = getPoints(VERTEX_DATA, line, br); + mesh.numOfVerts = mesh.vertices.length; + } + + if (lineIs(TEXTURE_DATA, line)) { + if (mesh == null) + mesh = new Mesh(); + + mesh.texCoords = getTexCoords(TEXTURE_DATA, line, br); + mesh.hasTexture = true; + mesh.numTexCoords = mesh.texCoords.length; + } + + if (lineIs(NORMAL_DATA, line)) { + if (mesh == null) + mesh = new Mesh(); + + mesh.normals = getPoints(NORMAL_DATA, line, br); + } + + if (lineIs(FACE_DATA, line)) { + if (mesh == null) + mesh = new Mesh(); + + mesh.faces = getFaces(line, mesh, br); + mesh.numOfFaces = mesh.faces.length; + + model.addMesh(mesh); + mesh = new Mesh(); + } + + if (lineIs("mtllib ", line)){ + processMaterialLib(line); + } + + if (lineIs("usemtl ", line)) { + processMaterialType(line, mesh); + } + } + } + catch (IOException e) { + throw new ModelLoadException("Failed to find or read OBJ: " + stream); + } + model.addMesh(mesh); + mesh = null; + + System.out.println(this.bounds.toString()); + model.setBounds(this.bounds); + model.setCenterPoint(this.center); + + return model; + } + + private boolean lineIs(String type, String line) { + return line.startsWith(type); + } + + private Vec4[] getPoints(String prefix, String currLine, BufferedReader br) throws IOException { + Vector points = new Vector(); + boolean isVertices = prefix.equals(VERTEX_DATA); + + // we've already read in the first line (currLine) + // so go ahead and parse it + points.add(parsePoint(currLine)); + + // parse through the rest of the points + String line = null; + while((line = br.readLine()) != null) { + if (!lineIs(prefix, line)) + break; + + Vec4 point = parsePoint(line); + if (isVertices) { + // Calculate the bounds for the entire model + bounds.calc(point); + } + points.add(point); + } + + if (isVertices) { + // Calculate the center of the model + center.x = 0.5f * (bounds.max.x + bounds.min.x); + center.y = 0.5f * (bounds.max.y + bounds.min.y); + center.z = 0.5f * (bounds.max.z + bounds.min.z); + } + + // return the points + Vec4 values[] = new Vec4[points.size()]; + return points.toArray(values); + } + + private TexCoord[] getTexCoords(String prefix, String currLine, BufferedReader br) throws IOException { + Vector texCoords = new Vector(); + + String s[] = currLine.split("\\s+"); + TexCoord texCoord = new TexCoord(); + texCoord.u = Float.parseFloat(s[1]); + texCoord.v = Float.parseFloat(s[2]); + + texCoords.add(texCoord); + + // parse through the rest of the points + String line = null; + while((line = br.readLine()) != null) { + if (!lineIs(prefix, line)) + break; + + s = line.split("\\s+"); + + texCoord = new TexCoord(); + texCoord.u = Float.parseFloat(s[1]); + texCoord.v = Float.parseFloat(s[2]); + + texCoords.add(texCoord); + } + + // return the texture coordinates + TexCoord values[] = new TexCoord[texCoords.size()]; + return texCoords.toArray(values); + } + + private Face[] getFaces(String currLine, Mesh mesh, BufferedReader br) throws IOException { + Vector faces = new Vector(); + + faces.add(parseFace(currLine)); + + // parse through the rest of the faces + String line = null; + while((line = br.readLine()) != null) { + if (lineIs(SMOOTHING_GROUP, line)) { + continue; + } + else if (lineIs("usemtl ", line)) { + processMaterialType(line, mesh); + } + + else if (lineIs(FACE_DATA, line)) { + faces.add(parseFace(line)); + } + + else + break; + } + + // return the faces + Face values[] = new Face[faces.size()]; + return faces.toArray(values); + } + + private Face parseFace(String line) { + String s[] = line.split("\\s+"); + if (line.contains("//")) { // Pattern is present if obj has no texture + for (int loop=1; loop < s.length; loop++) { + s[loop] = s[loop].replaceAll("//","/-1/"); //insert -1 for missing vt data + } + } + + int vdata[] = new int[s.length-1]; + int vtdata[] = new int[s.length-1]; + int vndata[] = new int[s.length-1]; + Face face = new Face(s.length - 1); + + for (int loop = 1; loop < s.length; loop++) { + String s1 = s[loop]; + String[] temp = s1.split("/"); + + if (temp.length > 0) { // we have vertex data + if (Integer.valueOf(temp[0]) < 0) { + //TODO handle relative vertex data + } + else { + face.vertIndex[loop-1] = Integer.valueOf(temp[0]) - 1 - this.vertexTotal; + //System.out.println("found vertex index: " + face.vertIndex[loop-1]); + } + } + + if (temp.length > 1) { // we have texture data + if(Integer.valueOf(temp[1]) < 0) { + face.coordIndex[loop - 1] = 0; + } + else { + face.coordIndex[loop - 1] = Integer.valueOf(temp[1]) - 1 - this.textureTotal; + //System.out.println("found texture index: " + face.coordIndex[loop-1]); + } + } + + if (temp.length > 2) { // we have normal data + face.normalIndex[loop-1] = Integer.valueOf(temp[2]) - 1 - this.normalTotal; + //System.out.println("found normal index: " + face.normalIndex[loop-1]); + } + } + + return face; + } + + private Vec4 parsePoint(String line) { + Vec4 point = new Vec4(); + + final String s[] = line.split("\\s+"); + + point.x = Float.parseFloat(s[1]); + point.y = Float.parseFloat(s[2]); + point.z = Float.parseFloat(s[3]); + + return point; + } + + private String parseName(String line) { + String name; + + final String s[] = line.split("\\s+"); + + name = s[1]; + + return name; + } + + private void processMaterialLib(String mtlData) { + String s[] = mtlData.split("\\s+"); + + Material mat = new Material(); + InputStream stream = null; + try { + stream = ResourceRetriever.getResourceAsInputStream(baseDir + s[1]); + } catch (IOException ex) { + ex.printStackTrace(); + } + + if(stream == null) { + try { + stream = new FileInputStream(baseDir + s[1]); + } catch (FileNotFoundException ex) { + ex.printStackTrace(); + return; + } + } + loadMaterialFile(stream); + } + + private void processMaterialType(String line, Mesh mesh) { + String s[] = line.split("\\s+"); + + int materialID = -1; + boolean hasTexture = false; + + for(int i = 0; i < model.getNumberOfMaterials(); i++){ + Material mat = model.getMaterial(i); + + if(mat.strName.equals(s[1])){ + materialID = i; + if(mat.strFile != null) + hasTexture = true; + else + hasTexture = false; + break; + } + } + + if(materialID != -1) + mesh.materialID = materialID; + } + + public Material loadMaterialFile(InputStream stream) { + Material mat = null; + int texId = 0; + + try { + BufferedReader br = new BufferedReader(new InputStreamReader(stream)); + String line; + + while((line = br.readLine()) != null){ + + String parts[] = line.trim().split("\\s+"); + + if(parts[0].equals("newmtl")){ + if(mat != null) + model.addMaterial(mat); + + mat = new Material(); + mat.strName = parts[1]; + mat.textureId = texId++; + + } else if(parts[0].equals("Ks")) + mat.specularColor = parseColor(line); + + else if(parts[0].equals("Ns")) { + if (parts.length > 1) + mat.shininess = Float.valueOf(parts[1]); + } + else if(parts[0].equals("d")) + ; + else if(parts[0].equals("illum")) + ; + else if(parts[0].equals("Ka")) + mat.ambientColor = parseColor(line); + else if(parts[0].equals("Kd")) + mat.diffuseColor = parseColor(line); + else if(parts[0].equals("map_Kd")) { + if (parts.length > 1) + mat.strFile = /*baseDir + */parts[1]; + } + + else if(parts[0].equals("map_Ka")) { + if (parts.length > 1) + mat.strFile = /*baseDir + */parts[1]; + } + } + + br.close(); + model.addMaterial(mat); + + } catch (FileNotFoundException ex) { + ex.printStackTrace(); + } catch (IOException ioe) { + ioe.printStackTrace(); + } + return mat; + } + + private Color parseColor(String line) { + String parts[] = line.trim().split("\\s+"); + + Color color = new Color(Float.valueOf(parts[1]), + Float.valueOf(parts[2]),Float.valueOf(parts[3])); + + return color; + } + + public static void main(String[] args) { + WaveFrontLoader loader = new WaveFrontLoader(); + try { + loader.load("C:\\Documents and Settings\\RodgersGB\\My Documents\\Projects\\JOGLUTILS\\src\\net\\java\\joglutils\\examples\\models\\obj\\penguin.obj"); + } catch (ModelLoadException ex) { + ex.printStackTrace(); + } + } +} diff --git a/src/net/java/joglutils/model/loader/iLoader.java b/src/net/java/joglutils/model/loader/iLoader.java new file mode 100644 index 0000000..9405018 --- /dev/null +++ b/src/net/java/joglutils/model/loader/iLoader.java @@ -0,0 +1,21 @@ +/* + * iLoader.java + * + * Created on February 27, 2008, 10:36 PM + * + * To change this template, choose Tools | Template Manager + * and open the template in the editor. + */ + +package net.java.joglutils.model.loader; + +import net.java.joglutils.model.ModelLoadException; +import net.java.joglutils.model.geometry.Model; + +/** + * + * @author RodgersGB + */ +public interface iLoader { + public Model load(String path) throws ModelLoadException; +} -- cgit v1.2.3