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 .HashMap ;
28
- import java .util .Map ;
28
+ import java .util .Hashtable ;
29
29
30
30
/**
31
31
* This class is not part of the Processing API and should not be used
@@ -45,18 +45,21 @@ public class PShapeOBJ extends PShape {
45
45
* Initializes a new OBJ Object with the given filename.
46
46
*/
47
47
public PShapeOBJ (PApplet parent , String filename ) {
48
- this (parent , parent .createReader (filename ));
48
+ this (parent , parent .createReader (filename ), getBasePath ( parent , filename ) );
49
49
}
50
50
51
-
52
51
public PShapeOBJ (PApplet parent , BufferedReader reader ) {
52
+ this (parent , reader , "" );
53
+ }
54
+
55
+ public PShapeOBJ (PApplet parent , BufferedReader reader , String basePath ) {
53
56
ArrayList <OBJFace > faces = new ArrayList <OBJFace >();
54
57
ArrayList <OBJMaterial > materials = new ArrayList <OBJMaterial >();
55
58
ArrayList <PVector > coords = new ArrayList <PVector >();
56
59
ArrayList <PVector > normals = new ArrayList <PVector >();
57
60
ArrayList <PVector > texcoords = new ArrayList <PVector >();
58
- parseOBJ (parent , reader ,
59
- faces , materials , coords , normals , texcoords );
61
+ parseOBJ (parent , basePath , reader ,
62
+ faces , materials , coords , normals , texcoords );
60
63
61
64
// The OBJ geometry is stored with each face in a separate child shape.
62
65
parent = null ;
@@ -95,10 +98,10 @@ protected PShapeOBJ(OBJFace face, OBJMaterial mtl,
95
98
vertexCount = face .vertIdx .size ();
96
99
vertices = new float [vertexCount ][12 ];
97
100
for (int j = 0 ; j < face .vertIdx .size (); j ++){
98
- int vertIdx , normIdx , texIdx ;
99
- PVector vert , norms , tex ;
101
+ int vertIdx , normIdx ;
102
+ PVector vert , norms ;
100
103
101
- vert = norms = tex = null ;
104
+ vert = norms = null ;
102
105
103
106
vertIdx = face .vertIdx .get (j ).intValue () - 1 ;
104
107
vert = coords .get (vertIdx );
@@ -110,13 +113,6 @@ protected PShapeOBJ(OBJFace face, OBJMaterial mtl,
110
113
}
111
114
}
112
115
113
- if (j < face .texIdx .size ()) {
114
- texIdx = face .texIdx .get (j ).intValue () - 1 ;
115
- if (-1 < texIdx ) {
116
- tex = texcoords .get (texIdx );
117
- }
118
- }
119
-
120
116
vertices [j ][X ] = vert .x ;
121
117
vertices [j ][Y ] = vert .y ;
122
118
vertices [j ][Z ] = vert .z ;
@@ -132,13 +128,23 @@ protected PShapeOBJ(OBJFace face, OBJMaterial mtl,
132
128
vertices [j ][PGraphics .NZ ] = norms .z ;
133
129
}
134
130
135
- if (tex != null ) {
136
- vertices [j ][PGraphics .U ] = tex .x ;
137
- vertices [j ][PGraphics .V ] = tex .y ;
138
- }
139
-
140
131
if (mtl != null && mtl .kdMap != null ) {
132
+ // This face is textured.
133
+ int texIdx ;
134
+ PVector tex = null ;
135
+
136
+ if (j < face .texIdx .size ()) {
137
+ texIdx = face .texIdx .get (j ).intValue () - 1 ;
138
+ if (-1 < texIdx ) {
139
+ tex = texcoords .get (texIdx );
140
+ }
141
+ }
142
+
141
143
image = mtl .kdMap ;
144
+ if (tex != null ) {
145
+ vertices [j ][PGraphics .U ] = tex .x ;
146
+ vertices [j ][PGraphics .V ] = tex .y ;
147
+ }
142
148
}
143
149
}
144
150
}
@@ -168,14 +174,14 @@ protected void addChildren(ArrayList<OBJFace> faces,
168
174
}
169
175
170
176
171
- static protected void parseOBJ (PApplet parent ,
177
+ static protected void parseOBJ (PApplet parent , String path ,
172
178
BufferedReader reader ,
173
179
ArrayList <OBJFace > faces ,
174
180
ArrayList <OBJMaterial > materials ,
175
181
ArrayList <PVector > coords ,
176
182
ArrayList <PVector > normals ,
177
183
ArrayList <PVector > texcoords ) {
178
- Map <String , Integer > mtlTable = new HashMap <String , Integer >();
184
+ Hashtable <String , Integer > mtlTable = new Hashtable <String , Integer >();
179
185
int mtlIdxCur = -1 ;
180
186
boolean readv , readvn , readvt ;
181
187
try {
@@ -184,7 +190,7 @@ static protected void parseOBJ(PApplet parent,
184
190
String line ;
185
191
String gname = "object" ;
186
192
while ((line = reader .readLine ()) != null ) {
187
- // Parse the line.
193
+ // Parse the line.
188
194
line = line .trim ();
189
195
if (line .equals ("" ) || line .indexOf ('#' ) == 0 ) {
190
196
// Empty line of comment, ignore line
@@ -216,35 +222,37 @@ static protected void parseOBJ(PApplet parent,
216
222
if (parts [0 ].equals ("v" )) {
217
223
// vertex
218
224
PVector tempv = new PVector (Float .valueOf (parts [1 ]).floatValue (),
219
- Float .valueOf (parts [2 ]).floatValue (),
220
- Float .valueOf (parts [3 ]).floatValue ());
225
+ Float .valueOf (parts [2 ]).floatValue (),
226
+ Float .valueOf (parts [3 ]).floatValue ());
221
227
coords .add (tempv );
222
228
readv = true ;
223
229
} else if (parts [0 ].equals ("vn" )) {
224
230
// normal
225
231
PVector tempn = new PVector (Float .valueOf (parts [1 ]).floatValue (),
226
- Float .valueOf (parts [2 ]).floatValue (),
227
- Float .valueOf (parts [3 ]).floatValue ());
232
+ Float .valueOf (parts [2 ]).floatValue (),
233
+ Float .valueOf (parts [3 ]).floatValue ());
228
234
normals .add (tempn );
229
235
readvn = true ;
230
236
} else if (parts [0 ].equals ("vt" )) {
231
237
// uv, inverting v to take into account Processing's inverted Y axis
232
238
// with respect to OpenGL.
233
239
PVector tempv = new PVector (Float .valueOf (parts [1 ]).floatValue (),
234
- 1 - Float .valueOf (parts [2 ]).
235
- floatValue ());
240
+ 1 - Float .valueOf (parts [2 ]).
241
+ floatValue ());
236
242
texcoords .add (tempv );
237
243
readvt = true ;
238
244
} else if (parts [0 ].equals ("o" )) {
239
245
// Object name is ignored, for now.
240
246
} else if (parts [0 ].equals ("mtllib" )) {
241
-
242
247
if (parts [1 ] != null ) {
243
248
String fn = parts [1 ];
249
+ if (fn .indexOf (File .separator ) == -1 && !path .equals ("" )) {
250
+ // Relative file name, adding the base path.
251
+ fn = path + File .separator + fn ;
252
+ }
244
253
BufferedReader mreader = parent .createReader (fn );
245
254
if (mreader != null ) {
246
- parseMTL (parent , fn , mreader , materials , mtlTable );
247
- mreader .close ();
255
+ parseMTL (parent , path , mreader , materials , mtlTable );
248
256
}
249
257
}
250
258
} else if (parts [0 ].equals ("g" )) {
@@ -332,10 +340,10 @@ static protected void parseOBJ(PApplet parent,
332
340
}
333
341
334
342
335
- static protected void parseMTL (PApplet parent , String mtlfn ,
343
+ static protected void parseMTL (PApplet parent , String path ,
336
344
BufferedReader reader ,
337
345
ArrayList <OBJMaterial > materials ,
338
- Map <String , Integer > materialsHash ) {
346
+ Hashtable <String , Integer > materialsHash ) {
339
347
try {
340
348
String line ;
341
349
OBJMaterial currentMtl = null ;
@@ -348,46 +356,39 @@ static protected void parseMTL(PApplet parent, String mtlfn,
348
356
if (parts [0 ].equals ("newmtl" )) {
349
357
// Starting new material.
350
358
String mtlname = parts [1 ];
351
- currentMtl = addMaterial (mtlname , materials , materialsHash );
352
- } else {
353
- if (currentMtl == null ) {
354
- currentMtl = addMaterial ("material" + materials .size (),
355
- materials , materialsHash );
356
- }
357
- if (parts [0 ].equals ("map_Kd" ) && parts .length > 1 ) {
358
- // Loading texture map.
359
- String texname = parts [1 ];
360
- currentMtl .kdMap = parent .loadImage (texname );
361
- if (currentMtl .kdMap == null ) {
362
- System .err .println ("The texture map \" " + texname + "\" " +
363
- "in the materials definition file \" " + mtlfn + "\" " +
364
- "is missing or inaccessible, make sure " +
365
- "the URL is valid or that the file has been " +
366
- "added to your sketch and is readable." );
367
- }
368
- } else if (parts [0 ].equals ("Ka" ) && parts .length > 3 ) {
369
- // The ambient color of the material
370
- currentMtl .ka .x = Float .valueOf (parts [1 ]).floatValue ();
371
- currentMtl .ka .y = Float .valueOf (parts [2 ]).floatValue ();
372
- currentMtl .ka .z = Float .valueOf (parts [3 ]).floatValue ();
373
- } else if (parts [0 ].equals ("Kd" ) && parts .length > 3 ) {
374
- // The diffuse color of the material
375
- currentMtl .kd .x = Float .valueOf (parts [1 ]).floatValue ();
376
- currentMtl .kd .y = Float .valueOf (parts [2 ]).floatValue ();
377
- currentMtl .kd .z = Float .valueOf (parts [3 ]).floatValue ();
378
- } else if (parts [0 ].equals ("Ks" ) && parts .length > 3 ) {
379
- // The specular color weighted by the specular coefficient
380
- currentMtl .ks .x = Float .valueOf (parts [1 ]).floatValue ();
381
- currentMtl .ks .y = Float .valueOf (parts [2 ]).floatValue ();
382
- currentMtl .ks .z = Float .valueOf (parts [3 ]).floatValue ();
383
- } else if ((parts [0 ].equals ("d" ) ||
384
- parts [0 ].equals ("Tr" )) && parts .length > 1 ) {
385
- // Reading the alpha transparency.
386
- currentMtl .d = Float .valueOf (parts [1 ]).floatValue ();
387
- } else if (parts [0 ].equals ("Ns" ) && parts .length > 1 ) {
388
- // The specular component of the Phong shading model
389
- currentMtl .ns = Float .valueOf (parts [1 ]).floatValue ();
359
+ currentMtl = new OBJMaterial (mtlname );
360
+ materialsHash .put (mtlname , new Integer (materials .size ()));
361
+ materials .add (currentMtl );
362
+ } else if (parts [0 ].equals ("map_Kd" ) && parts .length > 1 ) {
363
+ // Loading texture map.
364
+ String texname = parts [1 ];
365
+ if (texname .indexOf (File .separator ) == -1 && !path .equals ("" )) {
366
+ // Relative file name, adding the base path.
367
+ texname = path + File .separator + texname ;
390
368
}
369
+ currentMtl .kdMap = parent .loadImage (texname );
370
+ } else if (parts [0 ].equals ("Ka" ) && parts .length > 3 ) {
371
+ // The ambient color of the material
372
+ currentMtl .ka .x = Float .valueOf (parts [1 ]).floatValue ();
373
+ currentMtl .ka .y = Float .valueOf (parts [2 ]).floatValue ();
374
+ currentMtl .ka .z = Float .valueOf (parts [3 ]).floatValue ();
375
+ } else if (parts [0 ].equals ("Kd" ) && parts .length > 3 ) {
376
+ // The diffuse color of the material
377
+ currentMtl .kd .x = Float .valueOf (parts [1 ]).floatValue ();
378
+ currentMtl .kd .y = Float .valueOf (parts [2 ]).floatValue ();
379
+ currentMtl .kd .z = Float .valueOf (parts [3 ]).floatValue ();
380
+ } else if (parts [0 ].equals ("Ks" ) && parts .length > 3 ) {
381
+ // The specular color weighted by the specular coefficient
382
+ currentMtl .ks .x = Float .valueOf (parts [1 ]).floatValue ();
383
+ currentMtl .ks .y = Float .valueOf (parts [2 ]).floatValue ();
384
+ currentMtl .ks .z = Float .valueOf (parts [3 ]).floatValue ();
385
+ } else if ((parts [0 ].equals ("d" ) ||
386
+ parts [0 ].equals ("Tr" )) && parts .length > 1 ) {
387
+ // Reading the alpha transparency.
388
+ currentMtl .d = Float .valueOf (parts [1 ]).floatValue ();
389
+ } else if (parts [0 ].equals ("Ns" ) && parts .length > 1 ) {
390
+ // The specular component of the Phong shading model
391
+ currentMtl .ns = Float .valueOf (parts [1 ]).floatValue ();
391
392
}
392
393
}
393
394
}
@@ -396,27 +397,19 @@ static protected void parseMTL(PApplet parent, String mtlfn,
396
397
}
397
398
}
398
399
399
- protected static OBJMaterial addMaterial (String mtlname ,
400
- ArrayList <OBJMaterial > materials ,
401
- Map <String , Integer > materialsHash ) {
402
- OBJMaterial currentMtl = new OBJMaterial (mtlname );
403
- materialsHash .put (mtlname , Integer .valueOf (materials .size ()));
404
- materials .add (currentMtl );
405
- return currentMtl ;
406
- }
407
400
408
401
protected static int rgbaValue (PVector color ) {
409
402
return 0xFF000000 | ((int )(color .x * 255 ) << 16 ) |
410
- ((int )(color .y * 255 ) << 8 ) |
411
- (int )(color .z * 255 );
403
+ ((int )(color .y * 255 ) << 8 ) |
404
+ (int )(color .z * 255 );
412
405
}
413
406
414
407
415
408
protected static int rgbaValue (PVector color , float alpha ) {
416
409
return ((int )(alpha * 255 ) << 24 ) |
417
- ((int )(color .x * 255 ) << 16 ) |
418
- ((int )(color .y * 255 ) << 8 ) |
419
- (int )(color .z * 255 );
410
+ ((int )(color .x * 255 ) << 16 ) |
411
+ ((int )(color .y * 255 ) << 8 ) |
412
+ (int )(color .z * 255 );
420
413
}
421
414
422
415
@@ -438,6 +431,18 @@ static protected class OBJFace {
438
431
}
439
432
440
433
434
+ static protected String getBasePath (PApplet parent , String filename ) {
435
+ // Obtaining the path
436
+ File file = new File (parent .dataPath (filename ));
437
+ if (!file .exists ()) {
438
+ file = parent .sketchFile (filename );
439
+ }
440
+ String absolutePath = file .getAbsolutePath ();
441
+ return absolutePath .substring (0 ,
442
+ absolutePath .lastIndexOf (File .separator ));
443
+ }
444
+
445
+
441
446
// Stores a material defined in an MTL file.
442
447
static protected class OBJMaterial {
443
448
String name ;
@@ -462,4 +467,5 @@ static protected class OBJMaterial {
462
467
kdMap = null ;
463
468
}
464
469
}
465
- }
470
+ }
471
+
0 commit comments