Skip to content

Instantly share code, notes, and snippets.

@zao
Created August 14, 2012 18:15
Show Gist options
  • Save zao/3351414 to your computer and use it in GitHub Desktop.
Save zao/3351414 to your computer and use it in GitHub Desktop.
OBJ loading
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package se.zao.obj;
/**
*
* @author Lars
*/
public class IndexedMesh {
public float[] positions;
public float[] texcoords;
public float[] normals;
public short[] indices;
}
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package se.zao.obj;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import sun.rmi.runtime.Log;
/**
*
* @author Lars
*/
public class ObjLoader {
static class Vec3
{
float x, y, z;
Vec3()
{ this(0.0f, 0.0f, 0.0f); }
Vec3(float x, float y, float z)
{ this.x = x; this.y = y; this.z = z; }
void addTo(List<Float> list)
{
list.add(x);
list.add(y);
list.add(z);
}
}
static class Vec2
{
float x, y;
Vec2()
{ this(0.0f, 0.0f); }
Vec2(float x, float y)
{ this.x = x; this.y = y;}
void addTo(List<Float> list)
{
list.add(x);
list.add(y);
}
}
static class Triad
{
int positionIndex = 0, texcoordIndex = 0, normalIndex = 0;
boolean equals(Triad rhs)
{
return positionIndex == rhs.positionIndex &&
texcoordIndex == rhs.texcoordIndex &&
normalIndex == rhs.normalIndex;
}
}
static class RawFace
{
Triad[] vertices;
}
static class Triangle
{
Triad a, b, c;
Triangle(Triad a, Triad b, Triad c)
{ this.a = a; this.b = b; this.c = c; }
}
static class GatherStage
{
List<Vec3> positions = new ArrayList<>();
List<Vec2> texcoords = new ArrayList<>();
List<Vec3> normals = new ArrayList<>();
List<Triangle> triangles = new ArrayList<>();
}
static class IndexAssembleStage
{
List<Float> positionBuffer = new ArrayList<>();
List<Float> texcoordBuffer = new ArrayList<>();
List<Float> normalBuffer = new ArrayList<>();
List<Short> indexBuffer = new ArrayList<>();
}
public static IndexedMesh loadIndexed(String objData, boolean deduplicate)
{
GatherStage rawData = loadRaw(objData);
Map<Triad, Short> indexLookup = new HashMap<>();
IndexAssembleStage bakedData = new IndexAssembleStage();
for (Triangle tri : rawData.triangles)
{
Triad[] triads = new Triad[]{tri.a, tri.b, tri.c};
for (Triad t : triads)
{
Short idx = null;
if (deduplicate) {
idx = indexLookup.get(t);
}
if (idx == null) {
idx = (short)bakedData.indexBuffer.size();
Vec3 vp = rawData.positions.get(t.positionIndex-1);
Vec2 vt = rawData.texcoords.get(t.texcoordIndex-1);
Vec3 vn = rawData.normals.get(t.normalIndex-1);
vp.addTo(bakedData.positionBuffer);
vt.addTo(bakedData.texcoordBuffer);
vn.addTo(bakedData.normalBuffer);
if (deduplicate) {
indexLookup.put(t, idx);
}
}
bakedData.indexBuffer.add(idx);
}
}
IndexedMesh mesh = new IndexedMesh();
mesh.positions = new float[bakedData.positionBuffer.size()];
mesh.texcoords = new float[bakedData.texcoordBuffer.size()];
mesh.normals = new float[bakedData.normalBuffer.size()];
mesh.indices = new short[bakedData.indexBuffer.size()];
for (int i = 0; i < bakedData.positionBuffer.size(); ++i) {
mesh.positions[i] = bakedData.positionBuffer.get(i);
}
for (int i = 0; i < bakedData.texcoordBuffer.size(); ++i) {
mesh.texcoords[i] = bakedData.texcoordBuffer.get(i);
}
for (int i = 0; i < bakedData.normalBuffer.size(); ++i) {
mesh.normals[i] = bakedData.normalBuffer.get(i);
}
for (int i = 0; i < bakedData.indexBuffer.size(); ++i) {
mesh.indices[i] = bakedData.indexBuffer.get(i);
}
return mesh;
}
static GatherStage loadRaw(String objData)
{
GatherStage data = new GatherStage();
BufferedReader r = new BufferedReader(new StringReader(objData));
try
{
for (String line = r.readLine(); line != null; line = r.readLine())
{
String[] tokens = line.split("[#]");
if (tokens.length == 0) {
continue;
}
tokens = tokens[0].split("[ \t]+");
if (tokens.length < 2)
{
continue;
}
String cmd = tokens[0];
switch (cmd)
{
case "v":
{
assert tokens.length == 4;
data.positions.add(new Vec3(
Float.parseFloat(tokens[1]),
Float.parseFloat(tokens[2]),
Float.parseFloat(tokens[3])));
break;
}
case "vt":
{
assert tokens.length >= 3;
data.texcoords.add(new Vec2(
Float.parseFloat(tokens[1]),
Float.parseFloat(tokens[2])));
break;
}
case "vn":
{
assert tokens.length == 4;
data.normals.add(new Vec3(
Float.parseFloat(tokens[1]),
Float.parseFloat(tokens[2]),
Float.parseFloat(tokens[3])));
break;
}
case "f":
{
assert tokens.length >= 4;
int numVertices = tokens.length - 1;
RawFace face = new RawFace();
face.vertices = new Triad[numVertices];
for (int i = 0; i < numVertices; ++i)
{
String[] parts = tokens[i+1].split("[/]", -1);
Triad triad = new Triad();
if (parts.length >= 1) {
triad.positionIndex = Short.parseShort(parts[0]);
}
if (parts.length >= 2) {
triad.texcoordIndex = Short.parseShort(parts[1]);
}
if (parts.length >= 3) {
triad.normalIndex = Short.parseShort(parts[2]);
}
if (triad.positionIndex <= 0 || triad.texcoordIndex <= 0 || triad.normalIndex <= 0) {
// not bothering to handle reverse indexing
return null;
}
face.vertices[i] = triad;
}
Triad v0 = face.vertices[0], vPrev = face.vertices[1];
for (int i = 2; i < face.vertices.length; ++i)
{
Triad vCurrent = face.vertices[i];
Triangle tri = new Triangle(v0, vPrev, vCurrent);
vPrev = vCurrent;
data.triangles.add(tri);
}
break;
}
}
}
}
catch (IOException ex)
{
System.err.println(ex);
}
return data;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment