23
23
package processing .core ;
24
24
25
25
import java .io .BufferedReader ;
26
+ import java .io .File ;
26
27
import java .util .ArrayList ;
27
- import java .util .Hashtable ;
28
+ import java .util .HashMap ;
29
+ import java .util .Map ;
28
30
29
31
/**
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>
30
37
* OBJ loading implemented using code from Saito's OBJLoader library:
31
38
* http://code.google.com/p/saitoobjloader/
32
39
* and OBJReader from Ahmet Kizilay
@@ -39,17 +46,21 @@ public class PShapeOBJ extends PShape {
39
46
* Initializes a new OBJ Object with the given filename.
40
47
*/
41
48
public PShapeOBJ (PApplet parent , String filename ) {
42
- this (parent , parent .createReader (filename ));
49
+ this (parent , parent .createReader (filename ), getBasePath ( parent , filename ) );
43
50
}
44
51
45
-
46
52
public PShapeOBJ (PApplet parent , BufferedReader reader ) {
53
+ this (parent , reader , "" );
54
+ }
55
+
56
+ public PShapeOBJ (PApplet parent , BufferedReader reader , String basePath ) {
47
57
ArrayList <OBJFace > faces = new ArrayList <OBJFace >();
48
58
ArrayList <OBJMaterial > materials = new ArrayList <OBJMaterial >();
49
59
ArrayList <PVector > coords = new ArrayList <PVector >();
50
60
ArrayList <PVector > normals = new ArrayList <PVector >();
51
61
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 );
53
64
54
65
// The OBJ geometry is stored with each face in a separate child shape.
55
66
parent = null ;
@@ -88,10 +99,10 @@ protected PShapeOBJ(OBJFace face, OBJMaterial mtl,
88
99
vertexCount = face .vertIdx .size ();
89
100
vertices = new float [vertexCount ][12 ];
90
101
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 ;
93
104
94
- vert = norms = null ;
105
+ vert = norms = tex = null ;
95
106
96
107
vertIdx = face .vertIdx .get (j ).intValue () - 1 ;
97
108
vert = coords .get (vertIdx );
@@ -103,6 +114,13 @@ protected PShapeOBJ(OBJFace face, OBJMaterial mtl,
103
114
}
104
115
}
105
116
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
+
106
124
vertices [j ][X ] = vert .x ;
107
125
vertices [j ][Y ] = vert .y ;
108
126
vertices [j ][Z ] = vert .z ;
@@ -118,23 +136,13 @@ protected PShapeOBJ(OBJFace face, OBJMaterial mtl,
118
136
vertices [j ][PGraphics .NZ ] = norms .z ;
119
137
}
120
138
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
+ }
132
143
144
+ if (mtl != null && mtl .kdMap != null ) {
133
145
image = mtl .kdMap ;
134
- if (tex != null ) {
135
- vertices [j ][PGraphics .U ] = tex .x ;
136
- vertices [j ][PGraphics .V ] = tex .y ;
137
- }
138
146
}
139
147
}
140
148
}
@@ -164,14 +172,14 @@ protected void addChildren(ArrayList<OBJFace> faces,
164
172
}
165
173
166
174
167
- static protected void parseOBJ (PApplet parent ,
175
+ static protected void parseOBJ (PApplet parent , String path ,
168
176
BufferedReader reader ,
169
177
ArrayList <OBJFace > faces ,
170
178
ArrayList <OBJMaterial > materials ,
171
179
ArrayList <PVector > coords ,
172
180
ArrayList <PVector > normals ,
173
181
ArrayList <PVector > texcoords ) {
174
- Hashtable <String , Integer > mtlTable = new Hashtable <String , Integer >();
182
+ Map <String , Integer > mtlTable = new HashMap <String , Integer >();
175
183
int mtlIdxCur = -1 ;
176
184
boolean readv , readvn , readvt ;
177
185
try {
@@ -235,11 +243,16 @@ static protected void parseOBJ(PApplet parent,
235
243
// Object name is ignored, for now.
236
244
} else if (parts [0 ].equals ("mtllib" )) {
237
245
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 );
239
252
if (mreader != null ) {
240
- parseMTL (parent , mreader , materials , mtlTable );
253
+ parseMTL (parent , fn , path , mreader , materials , mtlTable );
254
+ mreader .close ();
241
255
}
242
- mreader .close ();
243
256
}
244
257
} else if (parts [0 ].equals ("g" )) {
245
258
gname = 1 < parts .length ? parts [1 ] : "" ;
@@ -326,10 +339,10 @@ static protected void parseOBJ(PApplet parent,
326
339
}
327
340
328
341
329
- static protected void parseMTL (PApplet parent ,
342
+ static protected void parseMTL (PApplet parent , String mtlfn , String path ,
330
343
BufferedReader reader ,
331
344
ArrayList <OBJMaterial > materials ,
332
- Hashtable <String , Integer > materialsHash ) {
345
+ Map <String , Integer > materialsHash ) {
333
346
try {
334
347
String line ;
335
348
OBJMaterial currentMtl = null ;
@@ -342,35 +355,53 @@ static protected void parseMTL(PApplet parent,
342
355
if (parts [0 ].equals ("newmtl" )) {
343
356
// Starting new material.
344
357
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
+ }
374
405
}
375
406
}
376
407
}
@@ -379,6 +410,14 @@ static protected void parseMTL(PApplet parent,
379
410
}
380
411
}
381
412
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
+ }
382
421
383
422
protected static int rgbaValue (PVector color ) {
384
423
return 0xFF000000 | ((int )(color .x * 255 ) << 16 ) |
@@ -413,6 +452,18 @@ static protected class OBJFace {
413
452
}
414
453
415
454
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
+
416
467
// Stores a material defined in an MTL file.
417
468
static protected class OBJMaterial {
418
469
String name ;
0 commit comments