1
- /* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
2
-
3
- /*
4
- Part of the Processing project - http://processing.org
5
-
6
- Copyright (c) 2012-16 The Processing Foundation
7
-
8
- This library is free software; you can redistribute it and/or
9
- modify it under the terms of the GNU Lesser General Public
10
- License version 2.1 as published by the Free Software Foundation.
11
-
12
- This library is distributed in the hope that it will be useful,
13
- but WITHOUT ANY WARRANTY; without even the implied warranty of
14
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15
- Lesser General Public License for more details.
16
-
17
- You should have received a copy of the GNU Lesser General
18
- Public License along with this library; if not, write to the
19
- Free Software Foundation, Inc., 59 Temple Place, Suite 330,
20
- Boston, MA 02111-1307 USA
21
- */
22
-
23
1
package processing .core ;
24
2
25
3
import java .io .BufferedReader ;
4
+ import java .io .File ;
26
5
import java .util .ArrayList ;
27
- import java .util .Hashtable ;
6
+ import java .util .HashMap ;
7
+ import java .util .Map ;
28
8
29
9
/**
10
+ * This class is not part of the Processing API and should not be used
11
+ * directly. Instead, use loadShape() and methods like it, which will make
12
+ * use of this class. Using this class directly will cause your code to break
13
+ * when combined with future versions of Processing.
14
+ * <p>
30
15
* OBJ loading implemented using code from Saito's OBJLoader library:
31
16
* http://code.google.com/p/saitoobjloader/
32
17
* and OBJReader from Ahmet Kizilay
@@ -39,17 +24,21 @@ public class PShapeOBJ extends PShape {
39
24
* Initializes a new OBJ Object with the given filename.
40
25
*/
41
26
public PShapeOBJ (PApplet parent , String filename ) {
42
- this (parent , parent .createReader (filename ));
27
+ this (parent , parent .createReader (filename ), getBasePath ( parent , filename ) );
43
28
}
44
29
45
-
46
30
public PShapeOBJ (PApplet parent , BufferedReader reader ) {
31
+ this (parent , reader , "" );
32
+ }
33
+
34
+ public PShapeOBJ (PApplet parent , BufferedReader reader , String basePath ) {
47
35
ArrayList <OBJFace > faces = new ArrayList <OBJFace >();
48
36
ArrayList <OBJMaterial > materials = new ArrayList <OBJMaterial >();
49
37
ArrayList <PVector > coords = new ArrayList <PVector >();
50
38
ArrayList <PVector > normals = new ArrayList <PVector >();
51
39
ArrayList <PVector > texcoords = new ArrayList <PVector >();
52
- parseOBJ (parent , reader , faces , materials , coords , normals , texcoords );
40
+ parseOBJ (parent , basePath , reader ,
41
+ faces , materials , coords , normals , texcoords );
53
42
54
43
// The OBJ geometry is stored with each face in a separate child shape.
55
44
parent = null ;
@@ -88,10 +77,10 @@ protected PShapeOBJ(OBJFace face, OBJMaterial mtl,
88
77
vertexCount = face .vertIdx .size ();
89
78
vertices = new float [vertexCount ][12 ];
90
79
for (int j = 0 ; j < face .vertIdx .size (); j ++){
91
- int vertIdx , normIdx ;
92
- PVector vert , norms ;
80
+ int vertIdx , normIdx , texIdx ;
81
+ PVector vert , norms , tex ;
93
82
94
- vert = norms = null ;
83
+ vert = norms = tex = null ;
95
84
96
85
vertIdx = face .vertIdx .get (j ).intValue () - 1 ;
97
86
vert = coords .get (vertIdx );
@@ -103,6 +92,13 @@ protected PShapeOBJ(OBJFace face, OBJMaterial mtl,
103
92
}
104
93
}
105
94
95
+ if (j < face .texIdx .size ()) {
96
+ texIdx = face .texIdx .get (j ).intValue () - 1 ;
97
+ if (-1 < texIdx ) {
98
+ tex = texcoords .get (texIdx );
99
+ }
100
+ }
101
+
106
102
vertices [j ][X ] = vert .x ;
107
103
vertices [j ][Y ] = vert .y ;
108
104
vertices [j ][Z ] = vert .z ;
@@ -118,23 +114,13 @@ protected PShapeOBJ(OBJFace face, OBJMaterial mtl,
118
114
vertices [j ][PGraphics .NZ ] = norms .z ;
119
115
}
120
116
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
- }
117
+ if (tex != null ) {
118
+ vertices [j ][PGraphics .U ] = tex .x ;
119
+ vertices [j ][PGraphics .V ] = tex .y ;
120
+ }
132
121
122
+ if (mtl != null && mtl .kdMap != null ) {
133
123
image = mtl .kdMap ;
134
- if (tex != null ) {
135
- vertices [j ][PGraphics .U ] = tex .x ;
136
- vertices [j ][PGraphics .V ] = tex .y ;
137
- }
138
124
}
139
125
}
140
126
}
@@ -164,14 +150,14 @@ protected void addChildren(ArrayList<OBJFace> faces,
164
150
}
165
151
166
152
167
- static protected void parseOBJ (PApplet parent ,
153
+ static protected void parseOBJ (PApplet parent , String path ,
168
154
BufferedReader reader ,
169
155
ArrayList <OBJFace > faces ,
170
156
ArrayList <OBJMaterial > materials ,
171
157
ArrayList <PVector > coords ,
172
158
ArrayList <PVector > normals ,
173
159
ArrayList <PVector > texcoords ) {
174
- Hashtable <String , Integer > mtlTable = new Hashtable <String , Integer >();
160
+ Map <String , Integer > mtlTable = new HashMap <String , Integer >();
175
161
int mtlIdxCur = -1 ;
176
162
boolean readv , readvn , readvt ;
177
163
try {
@@ -235,11 +221,16 @@ static protected void parseOBJ(PApplet parent,
235
221
// Object name is ignored, for now.
236
222
} else if (parts [0 ].equals ("mtllib" )) {
237
223
if (parts [1 ] != null ) {
238
- BufferedReader mreader = parent .createReader (parts [1 ]);
224
+ String fn = parts [1 ];
225
+ if (fn .indexOf (File .separator ) == -1 && !path .equals ("" )) {
226
+ // Relative file name, adding the base path.
227
+ fn = path + File .separator + fn ;
228
+ }
229
+ BufferedReader mreader = parent .createReader (fn );
239
230
if (mreader != null ) {
240
- parseMTL (parent , mreader , materials , mtlTable );
231
+ parseMTL (parent , fn , path , mreader , materials , mtlTable );
232
+ mreader .close ();
241
233
}
242
- mreader .close ();
243
234
}
244
235
} else if (parts [0 ].equals ("g" )) {
245
236
gname = 1 < parts .length ? parts [1 ] : "" ;
@@ -326,10 +317,10 @@ static protected void parseOBJ(PApplet parent,
326
317
}
327
318
328
319
329
- static protected void parseMTL (PApplet parent ,
320
+ static protected void parseMTL (PApplet parent , String mtlfn , String path ,
330
321
BufferedReader reader ,
331
322
ArrayList <OBJMaterial > materials ,
332
- Hashtable <String , Integer > materialsHash ) {
323
+ Map <String , Integer > materialsHash ) {
333
324
try {
334
325
String line ;
335
326
OBJMaterial currentMtl = null ;
@@ -342,35 +333,53 @@ static protected void parseMTL(PApplet parent,
342
333
if (parts [0 ].equals ("newmtl" )) {
343
334
// Starting new material.
344
335
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 ();
336
+ currentMtl = addMaterial (mtlname , materials , materialsHash );
337
+ } else {
338
+ if (currentMtl == null ) {
339
+ currentMtl = addMaterial ("material" + materials .size (),
340
+ materials , materialsHash );
341
+ }
342
+ if (parts [0 ].equals ("map_Kd" ) && parts .length > 1 ) {
343
+ // Loading texture map.
344
+ String texname = parts [1 ];
345
+ if (texname .indexOf (File .separator ) == -1 && !path .equals ("" )) {
346
+ // Relative file name, adding the base path.
347
+ texname = path + File .separator + texname ;
348
+ }
349
+
350
+ File file = new File (parent .dataPath (texname ));
351
+ if (file .exists ()) {
352
+ currentMtl .kdMap = parent .loadImage (texname );
353
+ } else {
354
+ System .err .println ("The texture map \" " + texname + "\" " +
355
+ "in the materials definition file \" " + mtlfn + "\" " +
356
+ "is missing or inaccessible, make sure " +
357
+ "the URL is valid or that the file has been " +
358
+ "added to your sketch and is readable." );
359
+ }
360
+ } else if (parts [0 ].equals ("Ka" ) && parts .length > 3 ) {
361
+ // The ambient color of the material
362
+ currentMtl .ka .x = Float .valueOf (parts [1 ]).floatValue ();
363
+ currentMtl .ka .y = Float .valueOf (parts [2 ]).floatValue ();
364
+ currentMtl .ka .z = Float .valueOf (parts [3 ]).floatValue ();
365
+ } else if (parts [0 ].equals ("Kd" ) && parts .length > 3 ) {
366
+ // The diffuse color of the material
367
+ currentMtl .kd .x = Float .valueOf (parts [1 ]).floatValue ();
368
+ currentMtl .kd .y = Float .valueOf (parts [2 ]).floatValue ();
369
+ currentMtl .kd .z = Float .valueOf (parts [3 ]).floatValue ();
370
+ } else if (parts [0 ].equals ("Ks" ) && parts .length > 3 ) {
371
+ // The specular color weighted by the specular coefficient
372
+ currentMtl .ks .x = Float .valueOf (parts [1 ]).floatValue ();
373
+ currentMtl .ks .y = Float .valueOf (parts [2 ]).floatValue ();
374
+ currentMtl .ks .z = Float .valueOf (parts [3 ]).floatValue ();
375
+ } else if ((parts [0 ].equals ("d" ) ||
376
+ parts [0 ].equals ("Tr" )) && parts .length > 1 ) {
377
+ // Reading the alpha transparency.
378
+ currentMtl .d = Float .valueOf (parts [1 ]).floatValue ();
379
+ } else if (parts [0 ].equals ("Ns" ) && parts .length > 1 ) {
380
+ // The specular component of the Phong shading model
381
+ currentMtl .ns = Float .valueOf (parts [1 ]).floatValue ();
382
+ }
374
383
}
375
384
}
376
385
}
@@ -379,6 +388,14 @@ static protected void parseMTL(PApplet parent,
379
388
}
380
389
}
381
390
391
+ protected static OBJMaterial addMaterial (String mtlname ,
392
+ ArrayList <OBJMaterial > materials ,
393
+ Map <String , Integer > materialsHash ) {
394
+ OBJMaterial currentMtl = new OBJMaterial (mtlname );
395
+ materialsHash .put (mtlname , Integer .valueOf (materials .size ()));
396
+ materials .add (currentMtl );
397
+ return currentMtl ;
398
+ }
382
399
383
400
protected static int rgbaValue (PVector color ) {
384
401
return 0xFF000000 | ((int )(color .x * 255 ) << 16 ) |
@@ -413,6 +430,18 @@ static protected class OBJFace {
413
430
}
414
431
415
432
433
+ static protected String getBasePath (PApplet parent , String filename ) {
434
+ // Obtaining the path
435
+ File file = new File (parent .dataPath (filename ));
436
+ if (!file .exists ()) {
437
+ file = parent .sketchFile (filename );
438
+ }
439
+ String absolutePath = file .getAbsolutePath ();
440
+ return absolutePath .substring (0 ,
441
+ absolutePath .lastIndexOf (File .separator ));
442
+ }
443
+
444
+
416
445
// Stores a material defined in an MTL file.
417
446
static protected class OBJMaterial {
418
447
String name ;
0 commit comments