package glmodel; import glapp.GLApp; import java.io.*; import java.util.ArrayList; /** * Reads a 3DS file (3D Studio Max) into ArrayLists. Data is read * into vertex, normal, texture coodinate and face lists. Rendering * and mesh manipulation are done by calling classes. * * @see GL_3DS_Importer.java */ public class GL_3DS_Reader { // These three hold the vertex, texture and normal coordinates // There is only one set of verts, textures, normals for entire file public ArrayList vertices = new ArrayList(); // Contains float[3] for each Vertex (XYZ) public ArrayList normals = new ArrayList(); // Contains float[3] for each normal public ArrayList textureCoords = new ArrayList(); // Contains float[3] for each texture map coord (UVW) public ArrayList faces = new ArrayList(); // Contains Face objects private int currentId; private int nextOffset; private String currentObjectName = null; private boolean endOfStream = false; public GL_3DS_Reader() { } public GL_3DS_Reader(String filename) { // Construct from file name loadobject(filename); } public void loadobject(String filename) { // load from String filename // Load it try { load3DSFromStream(GLApp.getInputStream(filename)); } catch (Exception e) { System.out.println("GL_3DS_Reader.loadobject(): Exception when reading file: " + filename + " " + e); } } // load an object // ASSUMES ONLY ONE object in 3ds file public boolean load3DSFromStream(InputStream inStream) { System.out.println(">> Importing from 3DS stream ..."); BufferedInputStream in = new BufferedInputStream(inStream); try { readHeader(in); if (currentId != 0x4D4D) { // 3DS file identifier System.out.println("Error: This is not a valid 3ds file."); return false; } while (!endOfStream) { readNext(in); // will load into currentObject } } catch (Throwable ignored) {} return true; } private String readString(InputStream in) throws IOException { String result = new String(); byte inByte; while ( (inByte = (byte) in.read()) != 0) result += (char) inByte; return result; } private int readInt(InputStream in) throws IOException { return in.read() | (in.read() << 8) | (in.read() << 16) | (in.read() << 24); } private int readShort(InputStream in) throws IOException { return (in.read() | (in.read() << 8)); } private float readFloat(InputStream in) throws IOException { return Float.intBitsToFloat(readInt(in)); } private void readHeader(InputStream in) throws IOException { currentId = readShort(in); nextOffset = readInt(in); endOfStream = currentId < 0; } private void readNext(InputStream in) throws IOException { readHeader(in); if (currentId == 0x3D3D) { // Mesh block return; } if (currentId == 0x4000) { // Object block currentObjectName = readString(in); System.out.println("GL_3DS_Reader: " + currentObjectName); return; } if (currentId == 0x4100) { // Triangular polygon object System.out.println("GL_3DS_Reader: start mesh object"); return; } if (currentId == 0x4110) { // Vertex list System.out.println("GL_3DS_Reader: read vertex list"); readVertexList(in); return; } if (currentId == 0x4120) { // Triangle Point list System.out.println("GL_3DS_Reader: read triangle list"); readPointList(in); return; } if (currentId == 0x4140) { // Mapping coordinates System.out.println("GL_3DS_Reader: read mapping coords"); readMappingCoordinates(in); return; } skip(in); } private void skip(InputStream in) throws IOException, OutOfMemoryError { for (int i = 0; (i < nextOffset - 6) && (!endOfStream); i++) { endOfStream = in.read() < 0; } } /** * 3D Studio Max Models have the Z-Axis pointing up. For compatibility * with OpenGL we need to flip the y values with the z values, and * negate z. This gives us z coming toward the viewer, x is horizontal * and y is vertical. */ private void readVertexList(InputStream in) throws IOException { float x, y, z, tmpy; int numVertices = readShort(in); for (int i = 0; i < numVertices; i++) { x = readFloat(in); y = readFloat(in); z = readFloat(in); //swap the Y and Z values tmpy = y; y = z; z = -tmpy; // add vertex to the list vertices.add( new float[] {x, y, z} ); } } /** * read triangles from file. A triangle is defined as three indices * into the vertex list. For consistency, I use the same face class * that the OBJ file uses, which also stores normal and texture coordinates * for each point of the face. In 3DS format, there is one texture * coordinate for each vertex, so the indices into the texture coordinate * list will be same as indices into the vertex list. We're not * loading normals so null that out. */ private void readPointList(InputStream in) throws IOException { int triangles = readShort(in); for (int i = 0; i < triangles; i++) { int[] vertexIDs = new int[3]; vertexIDs[0] = readShort(in); vertexIDs[1] = readShort(in); vertexIDs[2] = readShort(in); readShort(in); faces.add( new Face(vertexIDs,vertexIDs,null) ); } } private void readMappingCoordinates(InputStream in) throws IOException { int numVertices = readShort(in); for (int i = 0; i < numVertices; i++) { float[] uvw = new float[3]; uvw[0] = readFloat(in); uvw[1] = readFloat(in); uvw[2] = 0f; textureCoords.add( uvw ); } } }