00001 /* 00002 File: ParseObj.cc 00003 00004 Function: Read wavefront "obj" files 00005 00006 Author: Andrew Willmott 00007 00008 Notes: 00009 */ 00010 00011 #include <string.h> 00012 #include <unistd.h> 00013 #include "gcl/Scene.h" 00014 #include "gcl/SceneObjects.h" 00015 #include "gcl/Avars.h" 00016 #include "gcl/SceneLang.h" 00017 00018 scScenePtr ParseObjFile(const Char *filename); 00019 00020 static Void ReadIndexes(istream &s, Int &idx, Int &tidx, Int &nidx) 00021 { 00022 Char c; 00023 00024 idx = tidx = nidx = 0; 00025 if (isdigit(s.peek())) 00026 s >> idx; 00027 if (s.peek() == '/') 00028 { 00029 s.get(); 00030 if (isdigit(s.peek())) 00031 s >> tidx; 00032 if (s.peek() == '/') 00033 { 00034 s.get(); 00035 if (isdigit(s.peek())) 00036 s >> nidx; 00037 } 00038 } 00039 00040 idx--; 00041 nidx--; 00042 tidx--; 00043 } 00044 00045 /* 00046 Example material file: 00047 00048 newmtl Cube_0 00049 Ns 100 00050 d 1 00051 illum 2 00052 Ka .2 .2 .2 00053 Kd 0.8 0.8 0.8 00054 Ks 0 0 0 00055 map_Kd finishedx0001.jpg 00056 map_Ka finishedx0001.jpg 00057 */ 00058 00059 struct Mtl 00060 { 00061 Mtl() : flags(0) {}; 00062 00063 UInt32 flags; 00064 Colour kd; 00065 String texFile; 00066 00067 Void ApplyMtl(); 00068 }; 00069 00070 enum MtlFlags 00071 { 00072 hasTexture = 1, 00073 hasKd = 2, 00074 }; 00075 00076 Void Mtl::ApplyMtl() 00077 { 00078 if (flags & hasKd) 00079 slColour(kd); 00080 if (flags & hasTexture) 00081 { 00082 cerr << "setting texture " << texFile << endl; 00083 slEndFaces(); 00084 slTexture(texFile); 00085 slBeginFaces(); 00086 } 00087 } 00088 00089 typedef Array<Mtl> MtlList; 00090 00091 MtlList gMtlList; 00092 IntHash gMtlNames; 00093 00094 Int ParseMaterialFile(StrConst filename) 00095 { 00096 String token; 00097 ifstream s; 00098 Colour c; 00099 00100 cerr << "parsing material file " << filename << endl; 00101 s.open(filename); 00102 if (!s) 00103 { 00104 cerr << "(ParseMaterialFile) Cannot access " << filename << endl; 00105 return(-1); 00106 } 00107 00108 while (s) 00109 { 00110 if (token.ReadWord(s)) 00111 { 00112 if (token[0] == '#') 00113 ; 00114 else if (token == "newmtl") 00115 { 00116 String mtlName; 00117 00118 mtlName.ReadWord(s); 00119 cerr << "new material " << mtlName << endl; 00120 gMtlNames.SetItem(mtlName, gMtlList.NumItems()); 00121 gMtlList.Add(1); 00122 } 00123 else if (token == "Kd") 00124 { 00125 s >> c[0] >> c[1] >> c[2]; 00126 gMtlList.Last().kd = c; 00127 gMtlList.Last().flags |= hasKd; 00128 } 00129 else if (token == "map_Kd") 00130 { 00131 String texFile; 00132 00133 texFile.ReadWord(s); 00134 SubstituteEnvVars(texFile); 00135 00136 cerr << "texture file " << texFile << endl; 00137 gMtlList.Last().flags |= hasTexture; 00138 gMtlList.Last().texFile = texFile; 00139 } 00140 else 00141 cerr << "(ParseMaterialFile) *** Ignoring unknown token: " << token << endl; 00142 00143 token.ReadLine(s); // ignore rest of the line 00144 } 00145 } 00146 return(0); 00147 } 00148 00149 #define SMF_OLD 00150 00151 scScenePtr ParseObjFile(const Char *filename) 00152 // Parses wavefront-style .obj file. 00153 /* 00154 Format is: 'function args' on each line. 00155 00156 v x y z add vertex 00157 vt u v [w] add texture vert. 00158 vn i j k add normal 00159 f 1 5 3 2 define polygon 00160 indices are of the form v[/vt[/vn]] 00161 indices are 1-based. 00162 g group1 group2... set group names for following f's. 00163 o object name for following f's 00164 s n set smoothing group. 00165 usemtl material set material name for ... 00166 00167 Extensions: 00168 00169 c r g b add colour 00170 00171 */ 00172 { 00173 String token; 00174 Point vtx; 00175 Vector nrm; 00176 Coord tc; 00177 Colour clr; 00178 Int idx, tidx, nidx, cidx, vtxOffset; 00179 Int vertices, faces, colours, normals, texCoords; 00180 ifstream s; 00181 scScenePtr result; 00182 00183 // create default material 00184 gMtlList.Clear(); 00185 gMtlList.Add(1); 00186 00187 vtxOffset = -1; // for michael garland's SMF format 00188 00189 s.open(filename); 00190 if (!s) 00191 { 00192 cerr << "(ParseObjFile) Cannot access " << filename << endl; 00193 return(0); 00194 } 00195 00196 result = slBeginObject(filename); 00197 slPointList(); 00198 slBeginFaces(); 00199 vertices = colours = faces = normals = texCoords = 0; 00200 00201 while (s) 00202 { 00203 if (token.ReadWord(s)) 00204 { 00205 if (token[0] == '#') 00206 ; 00207 else if (token == "begin" || token == "end") 00208 { 00209 // michael's stack demarcation? ignore for now 00210 } 00211 else if (token == "%SMF") 00212 { 00213 GCLReal version; 00214 s >> version; 00215 if (version < 1.0) 00216 vtxOffset = 0; 00217 } 00218 #ifdef SMF_OLD 00219 else if (token == "t") 00220 { 00221 // SMF triangle : obsolete? 00222 s >> idx; slPointIndex(idx + vtxOffset); 00223 s >> idx; slPointIndex(idx + vtxOffset); 00224 s >> idx; slPointIndex(idx + vtxOffset); 00225 slFace(); 00226 faces++; 00227 } 00228 #endif 00229 else if (token == "set") 00230 { 00231 token.ReadWord(s); 00232 if (token == "vertex_correction") 00233 { 00234 s >> vtxOffset; 00235 vtxOffset = 1 - vtxOffset; 00236 } 00237 else 00238 cerr << "(ParseObjFile) *** Ignoring unknown set variable: " 00239 << token << endl; 00240 } 00241 00242 // standard wavefront stuff 00243 else if (token == "v") 00244 { 00245 s >> vtx[0] >> vtx[1] >> vtx[2]; 00246 slPoint(vtx); 00247 vertices++; 00248 } 00249 else if (token == "vt") 00250 { 00251 s >> tc[0] >> tc[1]; 00252 00253 if (texCoords == 0) 00254 slCoordList(); 00255 slCoord(tc); 00256 00257 texCoords++; 00258 } 00259 else if (token == "vn" || token == "n") 00260 { 00261 s >> nrm[0] >> nrm[1] >> nrm[2]; 00262 00263 if (normals == 0) 00264 slNormalList(); 00265 slNormal(nrm); 00266 00267 normals++; 00268 } 00269 else if (token == "vc" || token == "c") 00270 // extension to handle vertex colours 00271 { 00272 s >> clr[0] >> clr[1] >> clr[2]; 00273 if (colours == 0) 00274 slColourList(); 00275 slColour(clr); 00276 colours++; 00277 } 00278 else if (token == "g" || token == "s" || token == "o") 00279 { 00280 // ignore for now... 00281 } 00282 else if (token == "f" || token == "fo") 00283 { 00284 while(!IsEndOfLine(s)) 00285 { 00286 ReadIndexes(s, idx, tidx, nidx); 00287 00288 if (idx < 0) idx += vertices; 00289 if (tidx < 0) tidx += texCoords; 00290 if (nidx < 0) nidx += normals; 00291 00292 slPointIndex(idx); 00293 00294 if (tidx >= 0) 00295 slCoordIndex(tidx); 00296 if (nidx >= 0) 00297 slNormalIndex(nidx); 00298 } 00299 slFace(); 00300 faces++; 00301 } 00302 else if (token == "mtllib") 00303 { 00304 FileName mtlFile; 00305 00306 token.ReadWord(s); 00307 mtlFile.SetPath(filename); 00308 mtlFile.SetRelPath(token); 00309 ParseMaterialFile(mtlFile.GetPath()); 00310 } 00311 else if (token == "usemtl") 00312 { 00313 String material; 00314 00315 material.ReadWord(s); 00316 00317 slEndFaces(); 00318 00319 if (gMtlNames.ItemExists(material)) 00320 { 00321 // XXX 00322 Int mtlIdx = gMtlNames.GetItem(material); 00323 00324 gMtlList[mtlIdx].ApplyMtl(); 00325 } 00326 else if (material == "light") 00327 { 00328 slAttribute(new scAvarEmittance(Avar("light", 20, 5, 50), 00329 cWhite)); 00330 scColour(cBlack); 00331 } 00332 else if (material == "colour") 00333 { 00334 Colour mc; 00335 00336 s >> mc[0] >> mc[1] >> mc[2]; 00337 slColour(mc); 00338 } 00339 else if (material == "white") 00340 slColour(cWhite); 00341 else if (material == "black") 00342 slColour(cBlack); 00343 else if (material == "red") 00344 slColour(cRed); 00345 else if (material == "blue") 00346 slColour(cBlue); 00347 else if (material == "green") 00348 slColour(cGreen); 00349 else if (material == "grey") 00350 slAttribute(new scAvarColour(Avar("grey", 0.5, 0, 1), 00351 cWhite)); 00352 else 00353 { 00354 cerr << "(ParseObjFile) *** Warning: unrecognized material: " 00355 << material << endl; 00356 // set to default material 00357 gMtlNames.SetItem(material, 0); 00358 } 00359 00360 slBeginFaces(); 00361 } 00362 else 00363 cerr << "(ParseObjFile) *** Ignoring unknown token: " << token << endl; 00364 00365 token.ReadLine(s); // ignore rest of the line 00366 } 00367 } 00368 00369 slEndFaces(); 00370 slEndObject(); 00371 s.close(); 00372 00373 cerr << "Read " << vertices << " vertices, "; 00374 if (colours) cerr << colours << " colours, "; 00375 if (normals) cerr << normals << " normals, "; 00376 if (texCoords) cerr << texCoords << " texture coordinates, "; 00377 cerr << "and " << faces << " faces." << endl; 00378 00379 return(result); 00380 } 00381 00382 #ifdef CL_SGI_INST 00383 #pragma instantiate Array<Mtl> 00384 #endif 00385