package rajawali.parser; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import rajawali.BaseObject3D; import rajawali.materials.DiffuseMaterial; import rajawali.math.Number3D; import rajawali.renderer.RajawaliRenderer; import rajawali.util.RajLog; /** * 3DS object parser. This is a work in progress. Materials aren't parsed yet. * * @author dennis.ippel * @author lacasrac * */ public class Max3DSParser extends AMeshParser { private final int IDENTIFIER_3DS = 0x4D4D; private final int MESH_BLOCK = 0x3D3D; private final int OBJECT_BLOCK = 0x4000; private final int TRIMESH = 0x4100; private final int VERTICES = 0x4110; private final int FACES = 0x4120; private final int TEXCOORD = 0x4140; private final int TEX_MAP = 0xA200; private final int TRI_MATERIAL = 0x4130; private final int TEX_NAME = 0xA000; private final int TEX_FILENAME = 0xA300; private final int MATERIAL = 0xAFFF; private ArrayList<ArrayList<Number3D>> mVertices = new ArrayList<ArrayList<Number3D>>(); private ArrayList<Number3D[]> mNormals = new ArrayList<Number3D[]>(); private ArrayList<ArrayList<Number3D>> mVertNormals = new ArrayList<ArrayList<Number3D>>(); private ArrayList<ArrayList<Number3D>> mTexCoords = new ArrayList<ArrayList<Number3D>>(); private ArrayList<ArrayList<Integer>> mIndices = new ArrayList<ArrayList<Integer>>(); private ArrayList<String> mObjNames = new ArrayList<String>(); private int mChunkID; private int mChunkEndOffset; private boolean mEndReached = false; private int mObjects = -1; public Max3DSParser(RajawaliRenderer renderer, int resourceID) { super(renderer.getContext().getResources(), renderer.getTextureManager(), resourceID); } @Override public AMeshParser parse() throws ParsingException { RajLog.i("Start parsing 3DS"); InputStream stream = mResources.openRawResource(mResourceId); try { readHeader(stream); if (mChunkID != IDENTIFIER_3DS) { RajLog.e("Not a valid 3DS file"); return null; } while (!mEndReached) { readChunk(stream); } build(); if (mRootObject.getNumChildren() == 1) mRootObject = mRootObject.getChildAt(0); stream.close(); RajLog.i("End parsing 3DS"); } catch (IOException e) { RajLog.e("Error parsing"); throw new ParsingException(e); } return this; } void readChunk(InputStream stream) throws IOException { readHeader(stream); switch (mChunkID) { case MESH_BLOCK: break; case OBJECT_BLOCK: mObjects++; mObjNames.add(readString(stream)); break; case TRIMESH: break; case VERTICES: readVertices(stream); break; case FACES: readFaces(stream); break; case TEXCOORD: readTexCoords(stream); break; case TEX_NAME: // mCurrentMaterialKey = readString(stream); skipRead(stream); break; case TEX_FILENAME: // String fileName = readString(stream); // StringBuffer texture = new StringBuffer(packageID); // texture.append(":drawable/"); // // StringBuffer textureName = new StringBuffer(fileName.toLowerCase()); // int dotIndex = textureName.lastIndexOf("."); // if (dotIndex > -1) // texture.append(textureName.substring(0, dotIndex)); // else // texture.append(textureName); // // textureAtlas.addBitmapAsset(new BitmapAsset(mCurrentMaterialKey, texture.toString())); skipRead(stream); break; case TRI_MATERIAL: // String materialName = readString(stream); // int numFaces = readShort(stream); // // for (int i = 0; i < numFaces; i++) { // int faceIndex = readShort(stream); // co.faces.get(faceIndex).materialKey = materialName; // } skipRead(stream); break; case MATERIAL: break; case TEX_MAP: break; default: skipRead(stream); } } public void build() { int num = mVertices.size(); for(int j=0; j<num; ++j) { ArrayList<Integer> indices = mIndices.get(j); ArrayList<Number3D> vertices = mVertices.get(j); ArrayList<Number3D> texCoords = null; ArrayList<Number3D> vertNormals = mVertNormals.get(j); if(mTexCoords.size() > 0) texCoords = mTexCoords.get(j); int len = indices.size(); float[] aVertices = new float[len * 3]; float[] aNormals = new float[len * 3]; float[] aTexCoords = new float[len * 2]; int[] aIndices = new int[len]; int ic = 0; int itn = 0; int itc = 0; int ivi = 0; Number3D coord; Number3D texcoord; Number3D normal; for (int i = 0; i < len; i += 3) { int v1 = indices.get(i); int v2 = indices.get(i + 1); int v3 = indices.get(i + 2); coord = vertices.get(v1); aVertices[ic++] = coord.x; aVertices[ic++] = coord.y; aVertices[ic++] = coord.z; aIndices[ivi] = ivi++; coord = vertices.get(v2); aVertices[ic++] = coord.x; aVertices[ic++] = coord.y; aVertices[ic++] = coord.z; aIndices[ivi] = ivi++; coord = vertices.get(v3); aVertices[ic++] = coord.x; aVertices[ic++] = coord.y; aVertices[ic++] = coord.z; aIndices[ivi] = ivi++; if(texCoords != null && texCoords.size() > 0) { texcoord = texCoords.get(v1); aTexCoords[itc++] = texcoord.x; aTexCoords[itc++] = texcoord.y; texcoord = texCoords.get(v2); aTexCoords[itc++] = texcoord.x; aTexCoords[itc++] = texcoord.y; texcoord = texCoords.get(v3); aTexCoords[itc++] = texcoord.x; aTexCoords[itc++] = texcoord.y; } normal = vertNormals.get(v1); aNormals[itn++] = normal.x; aNormals[itn++] = normal.y; aNormals[itn++] = normal.z; normal = vertNormals.get(v2); aNormals[itn++] = normal.x; aNormals[itn++] = normal.y; aNormals[itn++] = normal.z; normal = vertNormals.get(v3); aNormals[itn++] = normal.x; aNormals[itn++] = normal.y; aNormals[itn++] = normal.z; } BaseObject3D targetObj = new BaseObject3D(mObjNames.get(j)); targetObj.setData(aVertices, aNormals, aTexCoords, null, aIndices); // -- diffuse material with random color. for now. DiffuseMaterial material = new DiffuseMaterial(); material.setUseColor(true); targetObj.setMaterial(material); targetObj.setColor(0xff000000 + (int)(Math.random() * 0xffffff)); mRootObject.addChild(targetObj); } } public void clear() { for(int i=0; i<mObjects; ++i) { mIndices.get(i).clear(); mVertNormals.get(i).clear(); mVertices.get(i).clear(); mTexCoords.get(i).clear(); } mIndices.clear(); mVertNormals.clear(); mVertices.clear(); mTexCoords.clear(); } protected void skipRead(InputStream stream) throws IOException { for (int i = 0; (i < mChunkEndOffset - 6) && !mEndReached; i++) { mEndReached = stream.read() < 0; } } protected void readVertices(InputStream buffer) throws IOException { float x, y, z; int numVertices = readShort(buffer); ArrayList<Number3D> vertices = new ArrayList<Number3D>(); for (int i = 0; i < numVertices; i++) { x = readFloat(buffer); y = readFloat(buffer); z = readFloat(buffer); vertices.add(new Number3D(x, y, z)); } mVertices.add(vertices); } protected void readTexCoords(InputStream buffer) throws IOException { int numVertices = readShort(buffer); ArrayList<Number3D> texCoords = new ArrayList<Number3D>(); for (int i = 0; i < numVertices; i++) { float x = readFloat(buffer); float y = 1-readFloat(buffer); texCoords.add(new Number3D(x, y, 0)); } mTexCoords.add(texCoords); } protected void readFaces(InputStream buffer) throws IOException { int triangles = readShort(buffer); Number3D[] normals = new Number3D[triangles]; ArrayList<Integer> indices = new ArrayList<Integer>(); for (int i = 0; i < triangles; i++) { int[] vertexIDs = new int[3]; vertexIDs[0] = readShort(buffer); vertexIDs[1] = readShort(buffer); vertexIDs[2] = readShort(buffer); readShort(buffer); indices.add(vertexIDs[0]); indices.add(vertexIDs[1]); indices.add(vertexIDs[2]); Number3D normal = calculateFaceNormal(vertexIDs); normals[i] = normal; } mNormals.add(new Number3D[triangles]); mIndices.add(indices); int numVertices = mVertices.get(mObjects).size(); int numIndices = indices.size(); ArrayList<Number3D> vertNormals = new ArrayList<Number3D>(); for (int i = 0; i < numVertices; i++) { Number3D vertexNormal = new Number3D(); for (int j = 0; j < numIndices; j += 3) { int id1 = indices.get(j); int id2 = indices.get(j + 1); int id3 = indices.get(j + 2); if (id1 == i || id2 == i || id3 == i) { vertexNormal.add(normals[j / 3]); } } vertexNormal.normalize(); vertNormals.add(vertexNormal); } mVertNormals.add(vertNormals); } private Number3D calculateFaceNormal(int[] vertexIDs) { ArrayList<Number3D> vertices = mVertices.get(mObjects); Number3D v1 = vertices.get(vertexIDs[0]); Number3D v2 = vertices.get(vertexIDs[2]); Number3D v3 = vertices.get(vertexIDs[1]); Number3D vector1 = Number3D.subtract(v2, v1); Number3D vector2 = Number3D.subtract(v3, v1); Number3D normal = Number3D.cross(vector1, vector2); normal.normalize(); return normal; } protected void readHeader(InputStream stream) throws IOException { mChunkID = readShort(stream); mChunkEndOffset = readInt(stream); mEndReached = mChunkID < 0; } protected String readString(InputStream stream) throws IOException { String result = new String(); byte inByte; while ((inByte = (byte) stream.read()) != 0) result += (char) inByte; return result; } protected int readInt(InputStream stream) throws IOException { return stream.read() | (stream.read() << 8) | (stream.read() << 16) | (stream.read() << 24); } protected int readShort(InputStream stream) throws IOException { return (stream.read() | (stream.read() << 8)); } protected float readFloat(InputStream stream) throws IOException { return Float.intBitsToFloat(readInt(stream)); } }