Skip to content

Commit 858b36c

Browse files
committed
ported OBJ fixes
1 parent 6433aca commit 858b36c

File tree

1 file changed

+109
-58
lines changed

1 file changed

+109
-58
lines changed

core/src/processing/core/PShapeOBJ.java

Lines changed: 109 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,17 @@
2323
package processing.core;
2424

2525
import java.io.BufferedReader;
26+
import java.io.File;
2627
import java.util.ArrayList;
27-
import java.util.Hashtable;
28+
import java.util.HashMap;
29+
import java.util.Map;
2830

2931
/**
32+
* This class is not part of the Processing API and should not be used
33+
* directly. Instead, use loadShape() and methods like it, which will make
34+
* use of this class. Using this class directly will cause your code to break
35+
* when combined with future versions of Processing.
36+
* <p>
3037
* OBJ loading implemented using code from Saito's OBJLoader library:
3138
* http://code.google.com/p/saitoobjloader/
3239
* and OBJReader from Ahmet Kizilay
@@ -39,17 +46,21 @@ public class PShapeOBJ extends PShape {
3946
* Initializes a new OBJ Object with the given filename.
4047
*/
4148
public PShapeOBJ(PApplet parent, String filename) {
42-
this(parent, parent.createReader(filename));
49+
this(parent, parent.createReader(filename), getBasePath(parent, filename));
4350
}
4451

45-
4652
public PShapeOBJ(PApplet parent, BufferedReader reader) {
53+
this(parent, reader, "");
54+
}
55+
56+
public PShapeOBJ(PApplet parent, BufferedReader reader, String basePath) {
4757
ArrayList<OBJFace> faces = new ArrayList<OBJFace>();
4858
ArrayList<OBJMaterial> materials = new ArrayList<OBJMaterial>();
4959
ArrayList<PVector> coords = new ArrayList<PVector>();
5060
ArrayList<PVector> normals = new ArrayList<PVector>();
5161
ArrayList<PVector> texcoords = new ArrayList<PVector>();
52-
parseOBJ(parent, reader, faces, materials, coords, normals, texcoords);
62+
parseOBJ(parent, basePath, reader,
63+
faces, materials, coords, normals, texcoords);
5364

5465
// The OBJ geometry is stored with each face in a separate child shape.
5566
parent = null;
@@ -88,10 +99,10 @@ protected PShapeOBJ(OBJFace face, OBJMaterial mtl,
8899
vertexCount = face.vertIdx.size();
89100
vertices = new float[vertexCount][12];
90101
for (int j = 0; j < face.vertIdx.size(); j++){
91-
int vertIdx, normIdx;
92-
PVector vert, norms;
102+
int vertIdx, normIdx, texIdx;
103+
PVector vert, norms, tex;
93104

94-
vert = norms = null;
105+
vert = norms = tex = null;
95106

96107
vertIdx = face.vertIdx.get(j).intValue() - 1;
97108
vert = coords.get(vertIdx);
@@ -103,6 +114,13 @@ protected PShapeOBJ(OBJFace face, OBJMaterial mtl,
103114
}
104115
}
105116

117+
if (j < face.texIdx.size()) {
118+
texIdx = face.texIdx.get(j).intValue() - 1;
119+
if (-1 < texIdx) {
120+
tex = texcoords.get(texIdx);
121+
}
122+
}
123+
106124
vertices[j][X] = vert.x;
107125
vertices[j][Y] = vert.y;
108126
vertices[j][Z] = vert.z;
@@ -118,23 +136,13 @@ protected PShapeOBJ(OBJFace face, OBJMaterial mtl,
118136
vertices[j][PGraphics.NZ] = norms.z;
119137
}
120138

121-
if (mtl != null && mtl.kdMap != null) {
122-
// This face is textured.
123-
int texIdx;
124-
PVector tex = null;
125-
126-
if (j < face.texIdx.size()) {
127-
texIdx = face.texIdx.get(j).intValue() - 1;
128-
if (-1 < texIdx) {
129-
tex = texcoords.get(texIdx);
130-
}
131-
}
139+
if (tex != null) {
140+
vertices[j][PGraphics.U] = tex.x;
141+
vertices[j][PGraphics.V] = tex.y;
142+
}
132143

144+
if (mtl != null && mtl.kdMap != null) {
133145
image = mtl.kdMap;
134-
if (tex != null) {
135-
vertices[j][PGraphics.U] = tex.x;
136-
vertices[j][PGraphics.V] = tex.y;
137-
}
138146
}
139147
}
140148
}
@@ -164,14 +172,14 @@ protected void addChildren(ArrayList<OBJFace> faces,
164172
}
165173

166174

167-
static protected void parseOBJ(PApplet parent,
175+
static protected void parseOBJ(PApplet parent, String path,
168176
BufferedReader reader,
169177
ArrayList<OBJFace> faces,
170178
ArrayList<OBJMaterial> materials,
171179
ArrayList<PVector> coords,
172180
ArrayList<PVector> normals,
173181
ArrayList<PVector> texcoords) {
174-
Hashtable<String, Integer> mtlTable = new Hashtable<String, Integer>();
182+
Map<String, Integer> mtlTable = new HashMap<String, Integer>();
175183
int mtlIdxCur = -1;
176184
boolean readv, readvn, readvt;
177185
try {
@@ -235,11 +243,16 @@ static protected void parseOBJ(PApplet parent,
235243
// Object name is ignored, for now.
236244
} else if (parts[0].equals("mtllib")) {
237245
if (parts[1] != null) {
238-
BufferedReader mreader = parent.createReader(parts[1]);
246+
String fn = parts[1];
247+
if (fn.indexOf(File.separator) == -1 && !path.equals("")) {
248+
// Relative file name, adding the base path.
249+
fn = path + File.separator + fn;
250+
}
251+
BufferedReader mreader = parent.createReader(fn);
239252
if (mreader != null) {
240-
parseMTL(parent, mreader, materials, mtlTable);
253+
parseMTL(parent, fn, path, mreader, materials, mtlTable);
254+
mreader.close();
241255
}
242-
mreader.close();
243256
}
244257
} else if (parts[0].equals("g")) {
245258
gname = 1 < parts.length ? parts[1] : "";
@@ -326,10 +339,10 @@ static protected void parseOBJ(PApplet parent,
326339
}
327340

328341

329-
static protected void parseMTL(PApplet parent,
342+
static protected void parseMTL(PApplet parent, String mtlfn, String path,
330343
BufferedReader reader,
331344
ArrayList<OBJMaterial> materials,
332-
Hashtable<String, Integer> materialsHash) {
345+
Map<String, Integer> materialsHash) {
333346
try {
334347
String line;
335348
OBJMaterial currentMtl = null;
@@ -342,35 +355,53 @@ static protected void parseMTL(PApplet parent,
342355
if (parts[0].equals("newmtl")) {
343356
// Starting new material.
344357
String mtlname = parts[1];
345-
currentMtl = new OBJMaterial(mtlname);
346-
materialsHash.put(mtlname, new Integer(materials.size()));
347-
materials.add(currentMtl);
348-
} else if (parts[0].equals("map_Kd") && parts.length > 1) {
349-
// Loading texture map.
350-
String texname = parts[1];
351-
currentMtl.kdMap = parent.loadImage(texname);
352-
} else if (parts[0].equals("Ka") && parts.length > 3) {
353-
// The ambient color of the material
354-
currentMtl.ka.x = Float.valueOf(parts[1]).floatValue();
355-
currentMtl.ka.y = Float.valueOf(parts[2]).floatValue();
356-
currentMtl.ka.z = Float.valueOf(parts[3]).floatValue();
357-
} else if (parts[0].equals("Kd") && parts.length > 3) {
358-
// The diffuse color of the material
359-
currentMtl.kd.x = Float.valueOf(parts[1]).floatValue();
360-
currentMtl.kd.y = Float.valueOf(parts[2]).floatValue();
361-
currentMtl.kd.z = Float.valueOf(parts[3]).floatValue();
362-
} else if (parts[0].equals("Ks") && parts.length > 3) {
363-
// The specular color weighted by the specular coefficient
364-
currentMtl.ks.x = Float.valueOf(parts[1]).floatValue();
365-
currentMtl.ks.y = Float.valueOf(parts[2]).floatValue();
366-
currentMtl.ks.z = Float.valueOf(parts[3]).floatValue();
367-
} else if ((parts[0].equals("d") ||
368-
parts[0].equals("Tr")) && parts.length > 1) {
369-
// Reading the alpha transparency.
370-
currentMtl.d = Float.valueOf(parts[1]).floatValue();
371-
} else if (parts[0].equals("Ns") && parts.length > 1) {
372-
// The specular component of the Phong shading model
373-
currentMtl.ns = Float.valueOf(parts[1]).floatValue();
358+
currentMtl = addMaterial(mtlname, materials, materialsHash);
359+
} else {
360+
if (currentMtl == null) {
361+
currentMtl = addMaterial("material" + materials.size(),
362+
materials, materialsHash);
363+
}
364+
if (parts[0].equals("map_Kd") && parts.length > 1) {
365+
// Loading texture map.
366+
String texname = parts[1];
367+
if (texname.indexOf(File.separator) == -1 && !path.equals("")) {
368+
// Relative file name, adding the base path.
369+
texname = path + File.separator + texname;
370+
}
371+
372+
File file = new File(parent.dataPath(texname));
373+
if (file.exists()) {
374+
currentMtl.kdMap = parent.loadImage(texname);
375+
} else {
376+
System.err.println("The texture map \"" + texname + "\" " +
377+
"in the materials definition file \"" + mtlfn + "\" " +
378+
"is missing or inaccessible, make sure " +
379+
"the URL is valid or that the file has been " +
380+
"added to your sketch and is readable.");
381+
}
382+
} else if (parts[0].equals("Ka") && parts.length > 3) {
383+
// The ambient color of the material
384+
currentMtl.ka.x = Float.valueOf(parts[1]).floatValue();
385+
currentMtl.ka.y = Float.valueOf(parts[2]).floatValue();
386+
currentMtl.ka.z = Float.valueOf(parts[3]).floatValue();
387+
} else if (parts[0].equals("Kd") && parts.length > 3) {
388+
// The diffuse color of the material
389+
currentMtl.kd.x = Float.valueOf(parts[1]).floatValue();
390+
currentMtl.kd.y = Float.valueOf(parts[2]).floatValue();
391+
currentMtl.kd.z = Float.valueOf(parts[3]).floatValue();
392+
} else if (parts[0].equals("Ks") && parts.length > 3) {
393+
// The specular color weighted by the specular coefficient
394+
currentMtl.ks.x = Float.valueOf(parts[1]).floatValue();
395+
currentMtl.ks.y = Float.valueOf(parts[2]).floatValue();
396+
currentMtl.ks.z = Float.valueOf(parts[3]).floatValue();
397+
} else if ((parts[0].equals("d") ||
398+
parts[0].equals("Tr")) && parts.length > 1) {
399+
// Reading the alpha transparency.
400+
currentMtl.d = Float.valueOf(parts[1]).floatValue();
401+
} else if (parts[0].equals("Ns") && parts.length > 1) {
402+
// The specular component of the Phong shading model
403+
currentMtl.ns = Float.valueOf(parts[1]).floatValue();
404+
}
374405
}
375406
}
376407
}
@@ -379,6 +410,14 @@ static protected void parseMTL(PApplet parent,
379410
}
380411
}
381412

413+
protected static OBJMaterial addMaterial(String mtlname,
414+
ArrayList<OBJMaterial> materials,
415+
Map<String, Integer> materialsHash) {
416+
OBJMaterial currentMtl = new OBJMaterial(mtlname);
417+
materialsHash.put(mtlname, Integer.valueOf(materials.size()));
418+
materials.add(currentMtl);
419+
return currentMtl;
420+
}
382421

383422
protected static int rgbaValue(PVector color) {
384423
return 0xFF000000 | ((int)(color.x * 255) << 16) |
@@ -413,6 +452,18 @@ static protected class OBJFace {
413452
}
414453

415454

455+
static protected String getBasePath(PApplet parent, String filename) {
456+
// Obtaining the path
457+
File file = new File(parent.dataPath(filename));
458+
if (!file.exists()) {
459+
file = parent.sketchFile(filename);
460+
}
461+
String absolutePath = file.getAbsolutePath();
462+
return absolutePath.substring(0,
463+
absolutePath.lastIndexOf(File.separator));
464+
}
465+
466+
416467
// Stores a material defined in an MTL file.
417468
static protected class OBJMaterial {
418469
String name;

0 commit comments

Comments
 (0)