/*
* Copyright (c) 2013, Oskar Veerhoek
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those
* of the authors and should not be interpreted as representing official policies,
* either expressed or implied, of the FreeBSD Project.
*/
package utility;
import org.lwjgl.BufferUtils;
import org.lwjgl.util.vector.Vector2f;
import org.lwjgl.util.vector.Vector3f;
import org.newdawn.slick.opengl.TextureLoader;
import java.io.*;
import java.nio.FloatBuffer;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL15.*;
/**
* @author Oskar
*/
public class OBJLoader {
public static int createDisplayList(Model m) {
int displayList = glGenLists(1);
glNewList(displayList, GL_COMPILE);
{
glMaterialf(GL_FRONT, GL_SHININESS, 120);
glColor3f(0.4f, 0.27f, 0.17f);
glBegin(GL_TRIANGLES);
for (Model.Face face : m.getFaces()) {
if (face.hasNormals()) {
Vector3f n1 = m.getNormals().get(face.getNormalIndices()[0] - 1);
glNormal3f(n1.x, n1.y, n1.z);
}
Vector3f v1 = m.getVertices().get(face.getVertexIndices()[0] - 1);
glVertex3f(v1.x, v1.y, v1.z);
if (face.hasNormals()) {
Vector3f n2 = m.getNormals().get(face.getNormalIndices()[1] - 1);
glNormal3f(n2.x, n2.y, n2.z);
}
Vector3f v2 = m.getVertices().get(face.getVertexIndices()[1] - 1);
glVertex3f(v2.x, v2.y, v2.z);
if (face.hasNormals()) {
Vector3f n3 = m.getNormals().get(face.getNormalIndices()[2] - 1);
glNormal3f(n3.x, n3.y, n3.z);
}
Vector3f v3 = m.getVertices().get(face.getVertexIndices()[2] - 1);
glVertex3f(v3.x, v3.y, v3.z);
}
glEnd();
}
glEndList();
return displayList;
}
private static FloatBuffer reserveData(int size) {
return BufferUtils.createFloatBuffer(size);
}
private static float[] asFloats(Vector3f v) {
return new float[]{v.x, v.y, v.z};
}
public static int[] createVBO(Model model) {
int vboVertexHandle = glGenBuffers();
int vboNormalHandle = glGenBuffers();
// TODO: Implement materials with VBOs
FloatBuffer vertices = reserveData(model.getFaces().size() * 9);
FloatBuffer normals = reserveData(model.getFaces().size() * 9);
for (Model.Face face : model.getFaces()) {
vertices.put(asFloats(model.getVertices().get(face.getVertexIndices()[0] - 1)));
vertices.put(asFloats(model.getVertices().get(face.getVertexIndices()[1] - 1)));
vertices.put(asFloats(model.getVertices().get(face.getVertexIndices()[2] - 1)));
normals.put(asFloats(model.getNormals().get(face.getNormalIndices()[0] - 1)));
normals.put(asFloats(model.getNormals().get(face.getNormalIndices()[1] - 1)));
normals.put(asFloats(model.getNormals().get(face.getNormalIndices()[2] - 1)));
}
vertices.flip();
normals.flip();
glBindBuffer(GL_ARRAY_BUFFER, vboVertexHandle);
glBufferData(GL_ARRAY_BUFFER, vertices, GL_STATIC_DRAW);
glVertexPointer(3, GL_FLOAT, 0, 0L);
glBindBuffer(GL_ARRAY_BUFFER, vboNormalHandle);
glBufferData(GL_ARRAY_BUFFER, normals, GL_STATIC_DRAW);
glNormalPointer(GL_FLOAT, 0, 0L);
glBindBuffer(GL_ARRAY_BUFFER, 0);
return new int[]{vboVertexHandle, vboNormalHandle};
}
private static Vector3f parseVertex(String line) {
String[] xyz = line.split(" ");
float x = Float.valueOf(xyz[1]);
float y = Float.valueOf(xyz[2]);
float z = Float.valueOf(xyz[3]);
return new Vector3f(x, y, z);
}
private static Vector3f parseNormal(String line) {
String[] xyz = line.split(" ");
float x = Float.valueOf(xyz[1]);
float y = Float.valueOf(xyz[2]);
float z = Float.valueOf(xyz[3]);
return new Vector3f(x, y, z);
}
private static Model.Face parseFace(boolean hasNormals, String line) {
String[] faceIndices = line.split(" ");
int[] vertexIndicesArray = {Integer.parseInt(faceIndices[1].split("/")[0]),
Integer.parseInt(faceIndices[2].split("/")[0]), Integer.parseInt(faceIndices[3].split("/")[0])};
if (hasNormals) {
int[] normalIndicesArray = new int[3];
normalIndicesArray[0] = Integer.parseInt(faceIndices[1].split("/")[2]);
normalIndicesArray[1] = Integer.parseInt(faceIndices[2].split("/")[2]);
normalIndicesArray[2] = Integer.parseInt(faceIndices[3].split("/")[2]);
return new Model.Face(vertexIndicesArray, normalIndicesArray);
} else {
return new Model.Face((vertexIndicesArray));
}
}
public static Model loadModel(File f) throws IOException {
BufferedReader reader = new BufferedReader(new FileReader(f));
Model m = new Model();
String line;
while ((line = reader.readLine()) != null) {
String prefix = line.split(" ")[0];
if (prefix.equals("#")) {
continue;
} else if (prefix.equals("v")) {
m.getVertices().add(parseVertex(line));
} else if (prefix.equals("vn")) {
m.getNormals().add(parseNormal(line));
} else if (prefix.equals("f")) {
m.getFaces().add(parseFace(m.hasNormals(), line));
} else {
throw new RuntimeException("OBJ file contains line which cannot be parsed correctly: " + line);
}
}
reader.close();
return m;
}
public static int createTexturedDisplayList(Model m) {
int displayList = glGenLists(1);
glNewList(displayList, GL_COMPILE);
{
glBegin(GL_TRIANGLES);
for (Model.Face face : m.getFaces()) {
if (face.hasTextureCoordinates()) {
glMaterial(GL_FRONT, GL_DIFFUSE, BufferTools.asFlippedFloatBuffer(face.getMaterial()
.diffuseColour[0], face.getMaterial().diffuseColour[1],
face.getMaterial().diffuseColour[2], 1));
glMaterial(GL_FRONT, GL_AMBIENT, BufferTools.asFlippedFloatBuffer(face.getMaterial()
.ambientColour[0], face.getMaterial().ambientColour[1],
face.getMaterial().ambientColour[2], 1));
glMaterialf(GL_FRONT, GL_SHININESS, face.getMaterial().specularCoefficient);
}
if (face.hasNormals()) {
Vector3f n1 = m.getNormals().get(face.getNormalIndices()[0] - 1);
glNormal3f(n1.x, n1.y, n1.z);
}
if (face.hasTextureCoordinates()) {
Vector2f t1 = m.getTextureCoordinates().get(face.getTextureCoordinateIndices()[0] - 1);
glTexCoord2f(t1.x, t1.y);
}
Vector3f v1 = m.getVertices().get(face.getVertexIndices()[0] - 1);
glVertex3f(v1.x, v1.y, v1.z);
if (face.hasNormals()) {
Vector3f n2 = m.getNormals().get(face.getNormalIndices()[1] - 1);
glNormal3f(n2.x, n2.y, n2.z);
}
if (face.hasTextureCoordinates()) {
Vector2f t2 = m.getTextureCoordinates().get(face.getTextureCoordinateIndices()[1] - 1);
glTexCoord2f(t2.x, t2.y);
}
Vector3f v2 = m.getVertices().get(face.getVertexIndices()[1] - 1);
glVertex3f(v2.x, v2.y, v2.z);
if (face.hasNormals()) {
Vector3f n3 = m.getNormals().get(face.getNormalIndices()[2] - 1);
glNormal3f(n3.x, n3.y, n3.z);
}
if (face.hasTextureCoordinates()) {
Vector2f t3 = m.getTextureCoordinates().get(face.getTextureCoordinateIndices()[2] - 1);
glTexCoord2f(t3.x, t3.y);
}
Vector3f v3 = m.getVertices().get(face.getVertexIndices()[2] - 1);
glVertex3f(v3.x, v3.y, v3.z);
}
glEnd();
}
glEndList();
return displayList;
}
public static Model loadTexturedModel(File f) throws IOException {
BufferedReader reader = new BufferedReader(new FileReader(f));
Model m = new Model();
Model.Material currentMaterial = new Model.Material();
String line;
while ((line = reader.readLine()) != null) {
if (line.startsWith("#")) {
continue;
}
if (line.startsWith("mtllib ")) {
String materialFileName = line.split(" ")[1];
File materialFile = new File(f.getParentFile().getAbsolutePath() + "/" + materialFileName);
BufferedReader materialFileReader = new BufferedReader(new FileReader(materialFile));
String materialLine;
Model.Material parseMaterial = new Model.Material();
String parseMaterialName = "";
while ((materialLine = materialFileReader.readLine()) != null) {
if (materialLine.startsWith("#")) {
continue;
}
if (materialLine.startsWith("newmtl ")) {
if (!parseMaterialName.equals("")) {
m.getMaterials().put(parseMaterialName, parseMaterial);
}
parseMaterialName = materialLine.split(" ")[1];
parseMaterial = new Model.Material();
} else if (materialLine.startsWith("Ns ")) {
parseMaterial.specularCoefficient = Float.valueOf(materialLine.split(" ")[1]);
} else if (materialLine.startsWith("Ka ")) {
String[] rgb = materialLine.split(" ");
parseMaterial.ambientColour[0] = Float.valueOf(rgb[1]);
parseMaterial.ambientColour[1] = Float.valueOf(rgb[2]);
parseMaterial.ambientColour[2] = Float.valueOf(rgb[3]);
} else if (materialLine.startsWith("Ks ")) {
String[] rgb = materialLine.split(" ");
parseMaterial.specularColour[0] = Float.valueOf(rgb[1]);
parseMaterial.specularColour[1] = Float.valueOf(rgb[2]);
parseMaterial.specularColour[2] = Float.valueOf(rgb[3]);
} else if (materialLine.startsWith("Kd ")) {
String[] rgb = materialLine.split(" ");
parseMaterial.diffuseColour[0] = Float.valueOf(rgb[1]);
parseMaterial.diffuseColour[1] = Float.valueOf(rgb[2]);
parseMaterial.diffuseColour[2] = Float.valueOf(rgb[3]);
} else if (materialLine.startsWith("map_Kd")) {
parseMaterial.texture = TextureLoader.getTexture("PNG",
new FileInputStream(new File(f.getParentFile().getAbsolutePath() + "/" + materialLine
.split(" ")[1])));
} else {
System.err.println("[MTL] Unknown Line: " + materialLine);
}
}
m.getMaterials().put(parseMaterialName, parseMaterial);
materialFileReader.close();
} else if (line.startsWith("usemtl ")) {
currentMaterial = m.getMaterials().get(line.split(" ")[1]);
} else if (line.startsWith("v ")) {
String[] xyz = line.split(" ");
float x = Float.valueOf(xyz[1]);
float y = Float.valueOf(xyz[2]);
float z = Float.valueOf(xyz[3]);
m.getVertices().add(new Vector3f(x, y, z));
} else if (line.startsWith("vn ")) {
String[] xyz = line.split(" ");
float x = Float.valueOf(xyz[1]);
float y = Float.valueOf(xyz[2]);
float z = Float.valueOf(xyz[3]);
m.getNormals().add(new Vector3f(x, y, z));
} else if (line.startsWith("vt ")) {
String[] xyz = line.split(" ");
float s = Float.valueOf(xyz[1]);
float t = Float.valueOf(xyz[2]);
m.getTextureCoordinates().add(new Vector2f(s, t));
} else if (line.startsWith("f ")) {
String[] faceIndices = line.split(" ");
int[] vertexIndicesArray = {Integer.parseInt(faceIndices[1].split("/")[0]),
Integer.parseInt(faceIndices[2].split("/")[0]), Integer.parseInt(faceIndices[3].split("/")[0])};
int[] textureCoordinateIndicesArray = {-1, -1, -1};
if (m.hasTextureCoordinates()) {
textureCoordinateIndicesArray[0] = Integer.parseInt(faceIndices[1].split("/")[1]);
textureCoordinateIndicesArray[1] = Integer.parseInt(faceIndices[2].split("/")[1]);
textureCoordinateIndicesArray[2] = Integer.parseInt(faceIndices[3].split("/")[1]);
}
int[] normalIndicesArray = {0, 0, 0};
if (m.hasNormals()) {
normalIndicesArray[0] = Integer.parseInt(faceIndices[1].split("/")[2]);
normalIndicesArray[1] = Integer.parseInt(faceIndices[2].split("/")[2]);
normalIndicesArray[2] = Integer.parseInt(faceIndices[3].split("/")[2]);
}
// Vector3f vertexIndices = new Vector3f(Float.valueOf(faceIndices[1].split("/")[0]),
// Float.valueOf(faceIndices[2].split("/")[0]),
// Float.valueOf(faceIndices[3].split("/")[0]));
// Vector3f normalIndices = new Vector3f(Float.valueOf(faceIndices[1].split("/")[2]),
// Float.valueOf(faceIndices[2].split("/")[2]),
// Float.valueOf(faceIndices[3].split("/")[2]));
m.getFaces().add(new Model.Face(vertexIndicesArray, normalIndicesArray,
textureCoordinateIndicesArray, currentMaterial));
} else if (line.startsWith("s ")) {
boolean enableSmoothShading = !line.contains("off");
m.setSmoothShadingEnabled(enableSmoothShading);
} else {
System.err.println("[OBJ] Unknown Line: " + line);
}
}
reader.close();
return m;
}
}