[ create a new paste ] login | about

Link: http://codepad.org/hpzwKNWK    [ raw code | fork ]

C, pasted on Nov 4:
package com.logic.engine.gfx.wavefront;

/*	ModelOBJ - Represents a 3D mesh, provides methods for loading, rendering, etc.
 * 	NOTE: Supports the wavefront OBJ format only
 * 
 * 	Written by Nick "SilverLogic" Brabant
 * 	©2010 - All Rights Reserved.
 */

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

import javax.microedition.khronos.opengles.GL10;

import com.logic.engine.gfx.GraphicsManager;
import com.logic.engine.gfx.Object3D;
import com.logic.engine.gfx.TextureManager;
import com.logic.engine.system.Debug;
import com.logic.engine.system.ResourceLoader;

public class ModelOBJ extends Object3D implements Object3D.GfxManager {
	// OBJ Groups - Represents individual objects of a model (i.e. the prop of a plane)
	private Collection<ModelOBJ> groupCollection = new ArrayList<ModelOBJ>();	// Holds all instances of OBJ groups
	
	// Coordinate Lists - These hold the arrays from each line, and are later referenced by index to build the buffers
	private ArrayList<float[]> vertexList = new ArrayList<float[]>();
	private ArrayList<float[]> textureList = new ArrayList<float[]>();
	private ArrayList<float[]> normalList = new ArrayList<float[]>();
	
	// Indexes - These hold the actual 'faces', each index contains the arrays which correspond to the lists index
	private ArrayList<short[]> vertexIndex = new ArrayList<short[]>();
	private ArrayList<short[]> textureIndex = new ArrayList<short[]>();
	private ArrayList<short[]> normalIndex = new ArrayList<short[]>();
	
	// Buffers - These are the arrays that actually hold all of the faces, and what gets passed to opengl for rendering
	private FloatBuffer indiceBuffer;
	private FloatBuffer textureBuffer;
	private FloatBuffer normalBuffer;
	
	@SuppressWarnings("unused")
	private ModelMTL mtlLoader;				// Used for loading the model mtl
	private Material material = new Material();		// Holds the specific material properties of this model
	private String mtlName;
	
	// This is simply used so that we don't try to pass empty buffers to opengl
	public boolean ready = false;
	
	// Keeps track of where this file is located (used when the mtlLoader definitions and any textures are loaded)
	private String filepath = "";
	
	// Constructor
	public ModelOBJ() {}
	
	// Overloaded Constructor - Takes a string to a filename in the assets folder, and loads the model
	public ModelOBJ(String fname) {
		LoadModel(fname);
	}
	
	// LoadModel - This method takes a string to a filename, retrieves a BufferedReader from the resource loader, and parses the file
	public void LoadModel(String fname) {
		// Load a BufferedReader from the specified asset file so we can read it line by line
		ResourceLoader res = new ResourceLoader();
		InputStreamReader isr = res.getStreamReader(fname);
		BufferedReader br = new BufferedReader(isr);
		
		// Get the local file path of the model (useful when retrieving mtlLoaders and textures)
		if(fname.lastIndexOf("/") > 0) { filepath = fname.substring(0, fname.lastIndexOf("/")) + "/"; }
		
		ParseModel(br);		// Parse the contents of the file and build the model
		
		try {
			br.close();		// Close the input stream and clean everything up
			isr.close();
			isr = null;
			br = null;
			res = null;
		} catch (IOException e) {
			Debug.throwError(e.toString() + "\nError closing out resources");
		}
	}
	
	// ParseModel - This method parses an obj file line by line, it should be passed a BufferedReader from LoadModel
	private void ParseModel(BufferedReader br) {
		String line;	// Holds the current line that we are working with
		String groupName = "";		// Holds the key (name) of the group we are currently in
		ModelOBJ tempModel = new ModelOBJ();		// This holds the current group we are working with
			try {
				while((line = br.readLine()) != null) {
					// # - Comment, we can skip these lines
					if(line.startsWith("#")) { continue; }
					
					// g - Group, this indicates that we need to create another model instance for these values
					if(line.startsWith("g")) {
						if(groupName != "") { AttachModel(tempModel, groupName); }	// If we were working in a group, add it to the list
						line = line.substring(2);
						groupName = line;	// We are now working in a new group
						tempModel = new ModelOBJ();	// Create a new object for this group
						continue;
					}
					
					// mtllib - Material Library, this specifies attributes about the current group
					if(line.startsWith("mtllib")) {
						line = line.substring(7);
						mtlLoader = new ModelMTL(filepath + line);
						continue;
					}
					
					// vt - Texture coordinates, we will load these into the textureList
					if(line.startsWith("vt")) {
						line = line.substring(3);
						float[] tempArr = getFloatArray(line, 2);
						tempArr[1] = 1.0f - tempArr[1];
						tempModel.textureList.add(tempArr);
						continue;
					}
					
					// vn - Normals, we will load these into the normalList
					if(line.startsWith("vn")) {
						line = line.substring(3);
						tempModel.normalList.add(getFloatArray(line, 3));
						continue;
					}
					
					// v - Vertices, we will load these into the vertexList
					if(line.startsWith("v")) {
						line = line.substring(2);
						tempModel.vertexList.add(getFloatArray(line, 3));
						continue;
					}
					
					// f - Face, here we fill the index lists
					if(line.startsWith("f")) {
						line = line.substring(2);
						String[] stemp = line.split(" ", 3);
						String[] v1 = stemp[0].split("/", 3);
						String[] v2 = stemp[1].split("/", 3);
						String[] v3 = stemp[2].split("/", 3);
						
						tempModel.vertexIndex.add(getIndexArray(v1, v2, v3, 0));	// Add vertice indexes to list
						tempModel.textureIndex.add(getIndexArray(v1, v2, v3, 1));	// Add texture indexes
						tempModel.normalIndex.add(getIndexArray(v1, v2, v3, 2));	// Add normals indexes
						continue;
					}

					// usemtl - lets us know what material group to use for this specific model group
					if(line.startsWith("usemtl")) {
						line = line.substring(7);
						//tempModel.material = mtlLoader.GetMaterial(line);
						//mtlName = tempModel.material.name;
						mtlName = line;
						continue;
					}
				}
			} catch (IOException e) {
				Debug.throwError("Unable to parse OBJ File");
			}
			
			tempModel.compileBuffers();		// We are finished parsing the file, time to fill the buffers
			
			// Not all models have groups, if a group was never assigned, we can just create the model directly
			if(groupName == "") {
				this.indiceBuffer = tempModel.indiceBuffer;
				this.textureBuffer = tempModel.textureBuffer;
				this.normalBuffer = tempModel.normalBuffer;
				this.material = tempModel.material;
				this.ready = true;
				GraphicsManager.removeObject(tempModel);	// We don't need this anymore, remove from the graphics manager
			} else {
				tempModel.setPosition(getPosX(), getPosY(), getPosZ()); // Since this is a child of our current model, render at the same coordinates
				tempModel.ready = true;
				AttachModel(tempModel, groupName);	// If a group was specified, attach the group to this model
			}
	}
	
	// getFloatArray - Just a method to cleanup the clutter in the ParseModel method, it splits a line into 3 float
	// values, and then returns it as a float[3], which can be added to an arraylist
	private float[] getFloatArray(String line, int size) {
		String[] stemp = line.split(" ", size);
		float[] ftemp = new float[size];
		for(int i = 0; i < size; i++) {
			ftemp[i] = Float.valueOf(stemp[i]).floatValue();
		}	
		return ftemp;
	}
	
	// getIndexArray, Another method to cleanup the clutter, this is pretty much the same thing as getFloatArray,
	// except you must specify an index depicting which Index you want returned
	// Index Key: 0 = vertexIndex, 1 = textureIndex
	private short[] getIndexArray(String[] v1, String[] v2, String[] v3, int index) {
		// Obtain the array containing the list index values (we must offset by 1, since arrays are zero based, and OBJ format has a base of 1
		short[] itemp = { (short)(Integer.valueOf(v1[index]).shortValue() - 1),
				   		  (short)(Integer.valueOf(v2[index]).shortValue() - 1),
				   		  (short)(Integer.valueOf(v3[index]).shortValue() - 1)};
		return itemp;
	}
	
	// AttachModel - This method is used both internally for managing object groups, and externally if you wish to
	// attach an outside model to this source (i.e. a weapon on a soldier, or a prop on a plane)
	public void AttachModel(ModelOBJ obj, String key) {
		groupCollection.add(obj);		// add the model to this models groupCollection
	}
	
	// compileBuffers - This is where the buffers that will be passed to opengl are built, each face in the index list
	// is referenced, then based on the index values, the buffer is built using the values at the indexed location
	private void compileBuffers() {
		short[] index = new short[3];	// holds the index array we are currently working with

		// Compile the indiceBuffer
		if(vertexIndex.size() > 0) {
			indiceBuffer = FloatBuffer.allocate(vertexIndex.size() * 9);	// Allocate the buffer
			Iterator<short[]> vertexIterator = vertexIndex.iterator();
			while(vertexIterator.hasNext()) {
				index = vertexIterator.next();	// Grab the next array of index values
				// The following lines inserts the 3 vertex's needed to complete the face
				indiceBuffer.put(vertexList.get(index[0]));
				indiceBuffer.put(vertexList.get(index[1]));
				indiceBuffer.put(vertexList.get(index[2]));
			}
		}
		
		// Compile the textureBuffer
		if(textureIndex.size() > 0) {
			textureBuffer = FloatBuffer.allocate(textureIndex.size() * 6);
			Iterator<short[]> textureIterator = textureIndex.iterator();
			while(textureIterator.hasNext()) {
				index = textureIterator.next();
				textureBuffer.put(textureList.get(index[0]));
				textureBuffer.put(textureList.get(index[1]));
				textureBuffer.put(textureList.get(index[2]));
			}
		}
		
		// Compile the normalBuffer
		if(normalIndex.size() > 0) {
			normalBuffer = FloatBuffer.allocate(normalIndex.size() * 9);
			Iterator<short[]> normalIterator = normalIndex.iterator();
			while(normalIterator.hasNext()) {
				index = normalIterator.next();
				normalBuffer.put(normalList.get(index[0]));
				normalBuffer.put(normalList.get(index[1]));
				normalBuffer.put(normalList.get(index[2]));
			}
		}
		
		// Just cleanup our un-needed variables, this will help free up a lot of memory
		vertexList.clear();
		textureList.clear();
		normalList.clear();
		vertexIndex.clear();
		textureIndex.clear();
		normalIndex.clear();
	}
	
	// We override the rotate method since this object may contain children, we need to apply rotation to all groups
	@Override
	public void Rotate(float x, float y, float z) {
		super.Rotate(x, y, z);
		Iterator<ModelOBJ> groupIterator = groupCollection.iterator();
		while(groupIterator.hasNext()) {
			groupIterator.next().Rotate(x, y, z);
		}
	}
	
	public void Duplicate(ModelOBJ obj) {
		indiceBuffer = obj.indiceBuffer;
		normalBuffer = obj.normalBuffer;
		textureBuffer = obj.textureBuffer;
		mtlName = obj.mtlName;
		ready = true;
	}
	
	@Override
	public void Init(GL10 gl) {
		super.Init(gl);
		// TODO init
	}
	
	// We can call this to destroy all children objects, and cleanup from the graphics manager
	@Override
	public void Cleanup() {
		ModelOBJ tempModel;
		Iterator<ModelOBJ> groupIterator = groupCollection.iterator();
		while(groupIterator.hasNext()) {
			tempModel = groupIterator.next();
			GraphicsManager.removeObject(tempModel);
			tempModel = null;
		}
		GraphicsManager.removeObject(this);
	}
	
	@Override
	public void Render(GL10 gl) {
		super.Render(gl);
		
		if(ready) {
			gl.glBindTexture(GL10.GL_TEXTURE_2D, TextureManager.GetTextureID(mtlName));
			gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
			gl.glEnableClientState(GL10.GL_NORMAL_ARRAY);
			gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
				gl.glVertexPointer(3, GL10.GL_FLOAT, 0, indiceBuffer);
				gl.glNormalPointer(GL10.GL_FLOAT, 0, normalBuffer);
				gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer);
				gl.glDrawArrays(GL10.GL_TRIANGLES, 0, (indiceBuffer.array().length / 3));
			gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
			gl.glDisableClientState(GL10.GL_NORMAL_ARRAY);
			gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
		}
		
		gl.glPopMatrix();
	}
}


Create a new paste based on this one


Comments: