All Downloads are FREE. Search and download functionalities are using the official Maven repository.

osm.map.worldwind.gl.obj.ObjLoader Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2017 United States Government as represented by the Administrator of the
 * National Aeronautics and Space Administration.
 * All Rights Reserved.
 */

/*
 * this code was published by user 'robotfire' on the WorldWind forum thread
 *   https://forum.worldwindcentral.com/forum/world-wind-java-forums/development-help/17605-collada-models-with-lighting
 * By permission of its creator it is put under the same license and copyright as WorldWind.
 */

package osm.map.worldwind.gl.obj;

import com.jogamp.opengl.util.texture.Texture;
import com.jogamp.opengl.util.texture.TextureIO;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.GZIPInputStream;

import com.jogamp.opengl.GL2;
import osm.map.worldwind.gl.obj.MtlLoader.Material;

public class ObjLoader {

	List vertexSets = new ArrayList<>();
	List vertexSetsNorms = new ArrayList<>();
	List vertexSetsTexs = new ArrayList<>();
	List faces = new ArrayList<>();
	int objectlist;
	float toppoint, bottompoint, leftpoint, rightpoint, farpoint, nearpoint;
	Map textureCache = new HashMap<>();
	BoundingBox bbox;
	private final static Logger logger = Logger.getLogger(ObjLoader.class.getName());

	String basePath;
	boolean flipTextureVertically;

	public ObjLoader(String objPath, GL2 gl, boolean centered, boolean flipTextureVertically) {
		this.flipTextureVertically = flipTextureVertically;
		String path="";
		objPath = objPath.replaceAll("\\\\", "/");
		int index = objPath.lastIndexOf("/");
		if(index < 0) {
			index = 0;
		}
		String name = objPath.substring(index+1);
		path=objPath.substring(0,index);
		this.init(path, name, gl, centered);
	}

	public ObjLoader(String basePath, String objPath, GL2 gl, boolean centered, boolean flipTextureVertically) {
		this.flipTextureVertically = flipTextureVertically;
		this.init(basePath, objPath, gl, centered);
	}

	private void init(String basePath, String objPath, GL2 gl, boolean centered) {
		try {
			if (basePath == null) {
				this.basePath = "";
			} else {
				this.basePath = basePath;
			}
			BufferedReader bufferedReader = null;
			InputStream is;
			try {
				is = getInputStream(basePath, objPath);
				bufferedReader = new BufferedReader(new InputStreamReader(is));
				loadObject(bufferedReader);
				if (centered) {
					centerit();
				}
				opengldrawtolist(gl);
				cleanup();
			} finally {
				if (bufferedReader != null) {
					bufferedReader.close();
				}
			}
			this.bbox = new BoundingBox(this.getXWidth(), this.getYHeight(), this.getZDepth(), this.bottompoint, centered);

		} catch (Exception e) {
			throw new RuntimeException("error loading model: " + basePath + "/" + objPath, e);
		}
	}

	InputStream getInputStream(String basePath, String objPath) throws IOException {
		String path = basePath + "/" + objPath;
		InputStream is = this.getClass().getResourceAsStream(path);
		if (is == null) {
			File f = new File(path);
			if (f.exists()) {
				is = new FileInputStream(f);
			}
		}
		if (path.endsWith(".gz")){
			is = new GZIPInputStream(is,8192);
		}
		return is;
	}

	public BoundingBox getBoundingBox() {
		return bbox;
	}

	private void cleanup() {
		vertexSets.clear();
		vertexSetsNorms.clear();
		vertexSetsTexs.clear();
		faces.clear();
	}

	private void loadObject(BufferedReader br) {
		String mtlID = null;
		MtlLoader mtlLoader = null;
		try {
			boolean firstpass = true;
			String newline;
			while ((newline = br.readLine()) != null) {
				if (newline.length() > 0) {
					newline = newline.trim();

					//Loads vertex coordinates
					if (newline.startsWith("v ")) {
						float coords[] = new float[4];
						newline = newline.substring(2, newline.length());
						StringTokenizer st = new StringTokenizer(newline, " ");
						for (int i = 0; st.hasMoreTokens(); i++) {
							coords[i] = Float.parseFloat(st.nextToken());
						}
						if (firstpass) {
							rightpoint = coords[0];
							leftpoint = coords[0];
							toppoint = coords[1];
							bottompoint = coords[1];
							nearpoint = coords[2];
							farpoint = coords[2];
							firstpass = false;
						}
						rightpoint = Math.max(coords[0], rightpoint);
						leftpoint = Math.min(coords[0], leftpoint);
						toppoint = Math.max(coords[1], toppoint);
						bottompoint = Math.min(coords[1], bottompoint);
						nearpoint = Math.max(coords[2], nearpoint);
						farpoint = Math.min(coords[2], farpoint);
						vertexSets.add(coords);
					} else //Loads vertex texture coordinates
					{
						if (newline.startsWith("vt")) {
							float coords[] = new float[4];
							newline = newline.substring(3, newline.length());
							StringTokenizer st = new StringTokenizer(newline, " ");
							for (int i = 0; st.hasMoreTokens(); i++) {
								coords[i] = Float.parseFloat(st.nextToken());
							}
							vertexSetsTexs.add(coords);
						} else //Loads vertex normals coordinates
						{
							if (newline.startsWith("vn")) {
								float coords[] = new float[4];
								newline = newline.substring(3, newline.length());
								StringTokenizer st = new StringTokenizer(newline, " ");
								for (int i = 0; st.hasMoreTokens(); i++) {
									coords[i] = Float.parseFloat(st.nextToken());
								}
								vertexSetsNorms.add(coords);
							} else if (newline.startsWith("f ")) { //Loads face coordinates
								newline = newline.substring(2, newline.length());
								StringTokenizer st = new StringTokenizer(newline, " ");
								int count = st.countTokens();
								int v[] = new int[count];
								int vt[] = new int[count];
								int vn[] = new int[count];
								for (int i = 0; i < count; i++) {
									char chars[] = st.nextToken().toCharArray();
									StringBuilder sb = new StringBuilder();
									char lc = 'x';
									for (int k = 0; k < chars.length; k++) {
										if (chars[k] == '/' && lc == '/') {
											sb.append('0');
										}
										lc = chars[k];
										sb.append(lc);
									}
									StringTokenizer st2 = new StringTokenizer(sb.toString(), "/");
									int num = st2.countTokens();
									v[i] = Integer.parseInt(st2.nextToken());
									if (num > 1) {
										vt[i] = Integer.parseInt(st2.nextToken());
									} else {
										vt[i] = 0;
									}
									if (num > 2) {
										vn[i] = Integer.parseInt(st2.nextToken());
									} else {
										vn[i] = 0;
									}
								}
								faces.add(new Face(mtlLoader.getMtl(mtlID), v, vn, vt));
							} else if (newline.startsWith("mtllib")) { //Loads materials
								mtlLoader = new MtlLoader(basePath, newline.substring(newline.indexOf(" ")).trim());
							} else if (newline.startsWith("usemtl")) { //Uses materials
								mtlID = newline.split("\\s+")[1];
							}
						}
					}
				}
			}
		} catch (IOException e) {
			System.out.println("Failed to read file: " + br.toString());
		} catch (NumberFormatException e) {
			System.out.println("Malformed OBJ file: " + br.toString() + "\r \r" + e.getMessage());
		}
		Collections.sort(faces);
	}

	private void centerit() {
		float xshift = (rightpoint - leftpoint) / 2.0F;
		float yshift = (toppoint - bottompoint) / 2.0F;
		float zshift = (nearpoint - farpoint) / 2.0F;
		for (int i = 0; i < vertexSets.size(); i++) {
			float coords[] = new float[4];
			coords[0] = (vertexSets.get(i))[0] - leftpoint - xshift;
			//coords[1] = (vertexSets.get(i))[1] - bottompoint - yshift;
			coords[1] = (vertexSets.get(i))[1] - bottompoint;
			coords[2] = (vertexSets.get(i))[2] - farpoint - zshift;
			vertexSets.set(i, coords);
		}

	}

	public double getMaxDimen() {
		double max = Math.max(getXWidth(), getYHeight());
		return Math.max(max, getZDepth());
	}

	public float getXWidth() {
		return rightpoint - leftpoint;
	}

	public float getYHeight() {
		return toppoint - bottompoint;
	}

	public float getZDepth() {
		return nearpoint - farpoint;
	}

	public int getPolygonCount() {
		return faces.size();
	}

	private void opengldrawtolist(GL2 gl) {
		String lastMapKd = "";
		Texture texture = null;
		this.objectlist = gl.glGenLists(1);

		gl.glNewList(objectlist, GL2.GL_COMPILE);
		Material mtl = null;
		for (Face face : faces) {
			if (mtl == null || !mtl.name.equals(face.mtl.name)) { //has mtl changed?  if so, set up the new mtl
				mtl = face.mtl;
				if (mtl.map_Kd == null) { //no texture?
					if (texture != null) { //disable previous texture if it's not null
						texture.disable(gl);
						texture = null;
						lastMapKd = "";
					}
				} else if (!lastMapKd.equals(mtl.map_Kd.toString())) { //yes texture, and it changed?
					if (texture != null) {
						texture.disable(gl);
					}
					texture = face.texture;
					texture.enable(gl);
					texture.bind(gl);
					gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_WRAP_T, GL2.GL_REPEAT);
					gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_WRAP_S, GL2.GL_REPEAT);
					gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_MAG_FILTER, GL2.GL_LINEAR);
					gl.glTexParameteri(GL2.GL_TEXTURE_2D, GL2.GL_TEXTURE_MIN_FILTER, GL2.GL_LINEAR);
					lastMapKd = mtl.map_Kd.toString();
				}

				//determine color
				gl.glEnable(GL2.GL_COLOR_MATERIAL);
				gl.glBlendFunc(GL2.GL_SRC_ALPHA, GL2.GL_ONE_MINUS_SRC_ALPHA); //enable alpha (transparency) channel
				gl.glEnable(GL2.GL_BLEND); //and blending
				float[] color = lighten(new float[]{Math.min(1, mtl.Kd[0] + mtl.Ka[0]),
					Math.min(1, mtl.Kd[1] + mtl.Ka[1]), Math.min(1, mtl.Kd[2] + mtl.Ka[2])}, 0.15f);
				gl.glColor4f(color[0], color[1], color[2], mtl.d);
			}

			//draw the polygons for this face
			gl.glBegin(face.polyType);
			for (int w = 0; w < face.v.length; w++) {
				if (face.vn[w] != 0) {
					float[] floats = vertexSetsNorms.get(face.vn[w] - 1);
					gl.glNormal3f(floats[0], floats[1], floats[2]);
				}
				if (face.vt[w] != 0) {
					float[] floats = vertexSetsTexs.get(face.vt[w] - 1);
					if (flipTextureVertically) {
						gl.glTexCoord2f(floats[0], 1f - floats[1]);
					} else {
						gl.glTexCoord2f(floats[0], floats[1]);
					}
				}
				float[] floats = vertexSets.get(face.v[w] - 1);
				gl.glVertex3f(floats[0], floats[1], floats[2]);
			}
			gl.glEnd();
		}
		gl.glDisable(GL2.GL_COLOR_MATERIAL);
		if (texture != null) {
			texture.disable(gl);
		}
		gl.glEndList();
	}

	private float[] lighten(float[] color, float amount) {
		float r = Math.min(1, color[0] + amount);
		float g = Math.min(1, color[1] + amount);
		float b = Math.min(1, color[2] + amount);
		return new float[]{r, g, b};
	}

	public void opengldraw(GL2 gl) {
		gl.glCallList(objectlist);
	}

	private Texture getTexture(String map_Kd) throws IOException {
		if (map_Kd == null) {
			return null;
		}
		if (textureCache.get(map_Kd) == null) {
			InputStream is = null;
			try {
				is = this.getInputStream(basePath, map_Kd);
				String suffix = null;
				String tokens[] = map_Kd.split("\\.");
				if (tokens != null) {
					if (tokens.length > 1) {
						suffix = tokens[tokens.length - 1];
					}
				}
				Texture t = TextureIO.newTexture(is, false, suffix);
				textureCache.put(map_Kd, t);
			} finally {
				if (is != null) {
					is.close();
				}
			}
		}
		return textureCache.get(map_Kd);
	}

	public class Face implements Comparable {

		MtlLoader.Material mtl;
		int[] v; //face
		int[] vn; //normal
		int[] vt; //texture
		int polyType;
		Texture texture;

		public Face(MtlLoader.Material mtl, int[] v, int[] vn, int[] vt) {
			this.mtl = mtl;
			this.v = v;
			this.vn = vn;
			this.vt = vt;
			switch (v.length) {
				case 3:
					polyType = GL2.GL_TRIANGLES;
					break;
				case 4:
					polyType = GL2.GL_QUADS;
					break;
				default:
					polyType = GL2.GL_POLYGON;
					break;
			}
			if (mtl.map_Kd != null) {
				try {
					texture = getTexture(mtl.map_Kd);
				} catch (Exception e) {
					logger.log(Level.SEVERE, "Exception reading texture: " + mtl.map_Kd, e);
				}
			}
		}

		@Override
		public int compareTo(Face face) {
			if (this.mtl.d > face.mtl.d) { //draw opaque faces first
				return -1;
			} else if (this.mtl.d < face.mtl.d) {
				return 1;
			}

			if (this.texture == null && face.texture != null) { //draw non-textured faces first
				return -1;
			} else if (this.texture != null && face.texture == null) {
				return 1;
			} else if (this.texture != null && face.texture != null) { //order by texture name
				return this.mtl.map_Kd.compareTo(face.mtl.map_Kd);
			}
			return this.mtl.name.compareTo(face.mtl.name); //order by mtl name
		}

	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy