implementing a mtl loader in to a obj

Started by leejenshil, June 22, 2015, 09:22:36

Previous topic - Next topic

leejenshil

hello, i am programming a obj loader to load models into my game but the thing is i usually use materials on my models rather than textures (low poly doesn't look so good with textures so i use colors and shaders) so being able to load textures is kind of useless. can anyone point me in a direction on how to load .mtl file as well as getting all it references from the obj file (the .obj says what mtl file to use for what faces)

here is my code for the obj loader:

package Loaders;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.List;

import org.lwjgl.util.vector.Vector2f;
import org.lwjgl.util.vector.Vector3f;

import Models.RawModel;
import RenderEngine.ModelLoader;

public class OBJLoader {
	public static RawModel loadObjModel(String fileName, ModelLoader loader){
		FileReader fr = null;
		try {
			fr = new FileReader(new File("res/" + fileName + ".obj"));
		} catch (FileNotFoundException e) {
			System.err.println("Couldn't load file!");
			e.printStackTrace();
		}
		BufferedReader reader = new BufferedReader(fr);
		String line;
		List<Vector3f> vertices = new ArrayList<Vector3f>();
		List<Vector2f> textures = new ArrayList<Vector2f>();
		List<Vector3f> normals = new ArrayList<Vector3f>();
		List<Integer> indices = new ArrayList<Integer>();
		float[] verticesArray = null;
		float[] texturesArray = null;
		float[] normalsArray = null;
		int[] indicesArray = null;
		try{
			while(true){
				line = reader.readLine();
				String[] currentLine = line.split(" ");
				if(line.startsWith("v ")){
					Vector3f vertex = new Vector3f(Float.parseFloat(currentLine[1]),
							Float.parseFloat(currentLine[2]),
							Float.parseFloat(currentLine[3]));
					vertices.add(vertex);
				}else if(line.startsWith("vt ")){
					Vector2f texture = new Vector2f(Float.parseFloat(currentLine[1]),
							Float.parseFloat(currentLine[2]));
					textures.add(texture);
				}else if(line.startsWith("vn ")){
					Vector3f normal = new Vector3f(Float.parseFloat(currentLine[1]),
							Float.parseFloat(currentLine[2]),
							Float.parseFloat(currentLine[3]));
					normals.add(normal);
				}else if(line.startsWith("f ")){
					texturesArray = new float[vertices.size() * 2];
					normalsArray = new float[vertices.size() * 3];
					break;
				}
			}
			while(line!=null){
				if(!line.startsWith("f ")){
					line = reader.readLine();
					continue;
				}
				String[] currentLine = line.split(" ");
				String[] vertex1 = currentLine[1].split("/");
				String[] vertex2 = currentLine[2].split("/");
				String[] vertex3 = currentLine[3].split("/");
				
				processVertex(vertex1, indices, textures, normals, texturesArray, 
						normalsArray);
				processVertex(vertex2, indices, textures, normals, texturesArray, 
						normalsArray);
				processVertex(vertex3, indices, textures, normals, texturesArray, 
						normalsArray);
				line = reader.readLine();
			}
			reader.close();
		}catch(Exception e){
			e.printStackTrace();
		}
		
		verticesArray = new float[vertices.size()*3];
		indicesArray = new int[indices.size()];
		
		int vertexPointer = 0;
		for(Vector3f vertex:vertices){
			verticesArray[vertexPointer++] = vertex.x;
			verticesArray[vertexPointer++] = vertex.y;
			verticesArray[vertexPointer++] = vertex.z;
		}
		for(int i=0;i<indices.size();i++){
			indicesArray[i] = indices.get(i);
		}
		return loader.loadToVAO(verticesArray, texturesArray, normalsArray, indicesArray);
	}
	
	private static void processVertex(String[] vertexData, List<Integer> indices,
			List<Vector2f> textures, List<Vector3f> normals, float[] textureArray,
			float[] mormalArray){
		int currentVertexPointer = Integer.parseInt(vertexData[0]) -1;
		indices.add(currentVertexPointer);
		Vector2f currentTex = textures.get(Integer.parseInt(vertexData[1]) -1);
		textureArray[currentVertexPointer*2] = currentTex.x;
		textureArray[currentVertexPointer*2 + 1] = 1 - currentTex.y;
		Vector3f currentNormal = normals.get(Integer.parseInt(vertexData[2]) -1);
		mormalArray[currentVertexPointer*3] = currentNormal.x;
		mormalArray[currentVertexPointer*3+1] = currentNormal.y;
		mormalArray[currentVertexPointer*3+2] = currentNormal.z;		
	}
}


the loader to VAO:
public class ModelLoader {
	private List<Integer> vaos = new ArrayList<Integer>();
	private List<Integer> vbos = new ArrayList<Integer>();
	private List<Integer> textures = new ArrayList<Integer>();
	
	public RawModel loadToVAO(float[] positions,float[] textureCoords,float[] normals,
			int[] indices) {
		int vaoID = createVAO();
		bindIndicesBuffer(indices);
		vaos.add(vaoID);
		storeDataInAttributeList(0,3,positions);
		storeDataInAttributeList(1,2,textureCoords);
		storeDataInAttributeList(2,3,normals);
		unbindVAO();
		return new RawModel(vaoID, indices.length);
	}
	
	public int loadTexture(String fileName) {
		Texture texture = null;
		try {
			texture = TextureLoader.getTexture("PNG", new FileInputStream("res/" + fileName + ".png"));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		int textureID = texture.getTextureID();
		textures.add(textureID);
		return textureID;
	}
	
	public void cleanUp(){
		for(int vao:vaos) {
			GL30.glDeleteVertexArrays(vao);
			
		}
		for(int vbo:vbos) {
			GL15.glDeleteBuffers(vbo);
		}
		for(int texture:textures) {
			GL11.glDeleteTextures(texture);
		}
	}
	
	private int createVAO() {
		int vaoID = GL30.glGenVertexArrays();
		GL30.glBindVertexArray(vaoID);
		return vaoID;
	}
	
	private void storeDataInAttributeList(int attributeNumber, int corridinateSize, float[] data) {
		int vboID = GL15.glGenBuffers();
		vbos.add(vboID);
		GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboID);
		FloatBuffer buffer = storeDataInFloatBuffer(data);
		GL15.glBufferData(GL15.GL_ARRAY_BUFFER, buffer, GL15.GL_STATIC_DRAW);
		GL20.glVertexAttribPointer(attributeNumber, corridinateSize, GL11.GL_FLOAT, false, 0, 0);
		GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
	}
	
	private void unbindVAO() {
		GL30.glBindVertexArray(0);
	}
	
	private void bindIndicesBuffer (int[] indices) {
		int vboID = GL15.glGenBuffers();
		vbos.add(vboID);
		GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, vboID);
		IntBuffer buffer = storeDatainIntBuffer(indices);
		GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, buffer, GL15.GL_STATIC_DRAW);
		
	}
	
	private IntBuffer storeDatainIntBuffer (int[] data) {
		IntBuffer buffer = BufferUtils.createIntBuffer(data.length);
		buffer.put(data);
		buffer.flip();
		return buffer;
	}
	
	private FloatBuffer storeDataInFloatBuffer(float[] data) {
		FloatBuffer buffer = BufferUtils.createFloatBuffer(data.length);
		buffer.put(data);
		buffer.flip();
		return buffer;
	}
}


and the renderer:
public class ModelRenderer {
	
	private final static float FOV = 70;
	private final static float NEAR_PLANE = 0.1f;
	private final static float FAR_PLANE = 1000;
	
	private Matrix4f projectionMatrix;
	
	public void prepare() {
		GL11.glEnable(GL11.GL_DEPTH_TEST);
		GL11.glClearColor(0, 0.3f, 0, 1);
		GL11.glClear(GL11.GL_COLOR_BUFFER_BIT|GL11.GL_DEPTH_BUFFER_BIT);
	}
	
	public ModelRenderer(StaticShader shader){
		createProjectionMatrix();
		shader.start();
		shader.loadProjectionMatrix(projectionMatrix);
		shader.start();
	}
	
	public void render(Entity entity, StaticShader shader) {
		TexturedModel texturedModel = entity.getModel();
		RawModel model = texturedModel.getRawModel();
		GL30.glBindVertexArray(model.getVaoID());
		GL20.glEnableVertexAttribArray(0);
		GL20.glEnableVertexAttribArray(1);
		GL20.glEnableVertexAttribArray(2);
		Matrix4f transformationMatrix = Maths.createTransformationMatrix(
				entity.getPosition(), entity.getRotX(), entity.getRotY(),
				entity.getRotZ(), entity.getScale());
		shader.loadTransformationMatrix(transformationMatrix);
		GL13.glActiveTexture(GL13.GL_TEXTURE0);
		GL11.glBindTexture(GL11.GL_TEXTURE_2D, texturedModel.getTexture().getID());
		GL11.glDrawElements(GL11.GL_TRIANGLES, model.getVertexCount(), GL11.GL_UNSIGNED_INT, 0);
		GL20.glDisableVertexAttribArray(0);
		GL20.glDisableVertexAttribArray(1);
		GL20.glDisableVertexAttribArray(2);
		GL30.glBindVertexArray(0);
	}
	
	private void createProjectionMatrix() {
        float aspectRatio = (float) Display.getWidth() / (float) Display.getHeight();
        float y_scale = (float) ((1 / Math.tan(Math.toRadians(FOV / 2))) * aspectRatio);
        float x_scale = y_scale / aspectRatio;
        float frustum_length = FAR_PLANE - NEAR_PLANE;
        
        projectionMatrix = new Matrix4f();
        projectionMatrix.m00 = x_scale;
        projectionMatrix.m11 = y_scale;
        projectionMatrix.m22 = -((FAR_PLANE + NEAR_PLANE) / frustum_length);
        projectionMatrix.m23 = -1;
        projectionMatrix.m32 = -((2 * NEAR_PLANE * FAR_PLANE) / frustum_length);
        projectionMatrix.m33 = 0;
    }
}


and finally an example for what i mean by the mtl calling:
# r default_Rgn
usemtl Mat
f 20/3/3 19/2/2 3/1/1
f 21/6/6 20/5/5 4/4/4
f 22/9/9 21/8/8 5/7/7
f 23/12/12 22/11/11 6/10/10
f 24/15/15 23/14/14 7/13/13
f 25/18/18 24/17/17 8/16/16
f 26/21/21 25/20/20 9/19/19
f 27/24/24 26/23/23 10/22/22
f 28/27/27 27/26/26 11/25/25
f 29/30/30 28/29/29 12/28/28
f 30/33/33 29/32/32 13/31/31
f 31/36/36 30/35/35 14/34/34
f 32/39/39 31/38/38 15/37/37
f 33/42/42 32/41/41 16/40/40
f 34/45/45 33/44/44 17/43/43
f 19/48/48 34/47/47 18/46/46
f 36/51/51 35/50/50 19/49/49
f 37/54/54 36/53/53 20/52/52
f 38/57/57 37/56/56 21/55/55
f 39/60/60 38/59/59 22/58/58
f 40/63/63 39/62/62 23/61/61
f 41/66/66 40/65/65 24/64/64
f 42/69/69 41/68/68 25/67/67
f 43/72/72 42/71/71 26/70/70
f 44/75/75 43/74/74 27/73/73
f 45/78/78 44/77/77 28/76/76
f 46/81/81 45/80/80 29/79/79
f 47/84/84 46/83/83 30/82/82
f 48/87/87 47/86/86 31/85/85
f 49/90/90 48/89/89 32/88/88
f 50/93/93 49/92/92 33/91/91
f 35/96/96 50/95/95 34/94/94
f 52/99/99 51/98/98 35/97/97

Kai

Quotei usually use materials on my models rather than textures...

Both aren't necessarily opposite things. A material just defines "how" something should look like. That definition *can* include the use of some textures as the diffuse/specular or whichever term in whatever lighting model, or *can* just use defined colors. Other material definitions even make use of GPU shader code...

Well, you obviously need a reader for MTL files then. Have a look at this page for a full description.

Wavefront OBJ files use the "mtllib" command to reference material files in the file system relative to the .obj file.

These material files contain material definitions (which can actually be quite complicated) for the standard Phong ambient/diffuse/specular/emissive lighting model using either RGBA colors or texture references.
Those materials are then associated with names using the "newmtl" command within the MTL file.

A material is then referenced in the OBJ file using the "usemtl" command which names the material to be used.

That material then affects all subsequent faces (those with the "f" command) until any other next material is used.