import_obj.h
Go to the documentation of this file.
00001 /****************************************************************************
00002 * VCGLib                                                            o o     *
00003 * Visual and Computer Graphics Library                            o     o   *
00004 *                                                                _   O  _   *
00005 * Copyright(C) 2004                                                \/)\/    *
00006 * Visual Computing Lab                                            /\/|      *
00007 * ISTI - Italian National Research Council                           |      *
00008 *                                                                    \      *
00009 * All rights reserved.                                                      *
00010 *                                                                           *
00011 * This program is free software; you can redistribute it and/or modify      *
00012 * it under the terms of the GNU General Public License as published by      *
00013 * the Free Software Foundation; either version 2 of the License, or         *
00014 * (at your option) any later version.                                       *
00015 *                                                                           *
00016 * This program is distributed in the hope that it will be useful,           *
00017 * but WITHOUT ANY WARRANTY; without even the implied warranty of            *
00018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
00019 * GNU General Public License (http://www.gnu.org/licenses/gpl.txt)          *
00020 * for more details.                                                         *
00021 *                                                                           *
00022 ****************************************************************************/
00023 
00024 
00025 #ifndef __VCGLIB_IMPORT_OBJ
00026 #define __VCGLIB_IMPORT_OBJ
00027 
00028 #include <wrap/callback.h>
00029 #include <wrap/io_trimesh/io_mask.h>
00030 #include <wrap/io_trimesh/io_material.h>
00031 #include <wrap/io_trimesh/io_fan_tessellator.h>
00032 #ifdef __gl_h_
00033 #include <wrap/gl/glu_tesselator.h>
00034 #endif
00035 #include <vcg/space/color4.h>
00036 
00037 
00038 #include <fstream>
00039 #include <string>
00040 #include <vector>
00041 
00042 
00043 namespace vcg {
00044     namespace tri {
00045         namespace io {
00046 
00051             template <class OpenMeshType>
00052             class ImporterOBJ
00053             {
00054             public:
00055                 static int &MRGBLineCount(){static int _MRGBLineCount=0; return _MRGBLineCount;}
00056 
00057                 typedef typename OpenMeshType::VertexPointer VertexPointer;
00058                 typedef typename OpenMeshType::ScalarType ScalarType;
00059                 typedef typename OpenMeshType::VertexType VertexType;
00060                 typedef typename OpenMeshType::EdgeType   EdgeType;
00061                 typedef typename OpenMeshType::FaceType FaceType;
00062                 typedef typename OpenMeshType::VertexIterator VertexIterator;
00063                 typedef typename OpenMeshType::FaceIterator FaceIterator;
00064                 typedef typename OpenMeshType::CoordType CoordType;
00065 
00066                 class Info
00067                 {
00068                 public:
00069 
00070                     Info()
00071                     {
00072                         mask    = 0;
00073                         cb              = 0;
00074                         numTexCoords=0;
00075                     }
00076 
00078                     int mask;
00079 
00081                     // it returns the current position, and formats a string with a description of what th efunction is doing (loading vertexes, faces...)
00082                     CallBackPos *cb;
00083 
00085                     int numVertices;
00087                     int numEdges;
00090                     int numFaces;
00092                     int numTexCoords;
00094                     int numNormals;
00095 
00096                 }; // end class
00097 
00098 
00099                 //struct OBJFacet
00100                 //{
00101                 //  CoordType n;
00102                 //      CoordType t;
00103                 //  CoordType v[3];
00104                 //
00105                 //      short attr;  // material index
00106                 //};
00107                 struct ObjIndexedFace
00108                 {
00109                     void set(const int & num){v.resize(num);n.resize(num); t.resize(num);}
00110                     std::vector<int> v;
00111                     std::vector<int> n;
00112                     std::vector<int> t;
00113                     int tInd;
00114                     bool  edge[3];// useless if the face is a polygon, no need to have variable length array
00115                     Color4b c;
00116                 };
00117 
00118                 struct ObjEdge
00119                 {
00120                     int v0;
00121                     int v1;
00122                 };
00123 
00124                 struct ObjTexCoord
00125                 {
00126                     float u;
00127                     float v;
00128                 };
00129 
00130                 enum OBJError {
00131                     // Successfull opening
00132                     E_NOERROR                           = 0*2+0,  //  A*2+B  (A position of correspondig string in the array, B=1 if not critical)
00133 
00134                     // Non Critical Errors (only odd numbers)
00135                     E_NON_CRITICAL_ERROR                = 0*2+1,
00136                     E_MATERIAL_FILE_NOT_FOUND           = 1*2+1,
00137                     E_MATERIAL_NOT_FOUND                = 2*2+1,
00138                     E_TEXTURE_NOT_FOUND                 = 3*2+1,
00139                     E_VERTICES_WITH_SAME_IDX_IN_FACE    = 4*2+1,
00140                     E_LESS_THAN_3_VERT_IN_FACE          = 5*2+1,
00141 
00142                     // Critical Opening Errors (only even numbers)
00143                     E_CANTOPEN                          = 6*2+0,
00144                     E_UNEXPECTED_EOF                     = 7*2+0,
00145                     E_ABORTED                           = 8*2+0,
00146                     E_NO_VERTEX                         = 9*2+0,
00147                     E_NO_FACE                           =10*2+0,
00148                     E_BAD_VERTEX_STATEMENT              =11*2+0,
00149                     E_BAD_VERT_TEX_STATEMENT            =12*2+0,
00150                     E_BAD_VERT_NORMAL_STATEMENT         =13*2+0,
00151                     E_BAD_VERT_INDEX                    =14*2+0,
00152                     E_BAD_VERT_TEX_INDEX                =15*2+0,
00153                     E_BAD_VERT_NORMAL_INDEX             =16*2+0,
00154                     E_LESS_THAN_4_VERT_IN_QUAD          =17*2+0
00155                 };
00156 
00157                 // to check if a given error is critical or not.
00158                 static bool ErrorCritical(int err)
00159                 {
00160                     if (err==0) return false;
00161                     if (err&1) return false;
00162                     return true;
00163                 }
00164 
00165                 static const char* ErrorMsg(int error)
00166                 {
00167                     const int MAXST = 18;
00168                     static const char* obj_error_msg[MAXST] =
00169                     {
00170                         /*  0 */ "No errors",
00171 
00172                         /*  1 */ "Material library file wrong or not found, a default white material is used",
00173                         /*  2 */ "Some materials definitions were not found, a default white material is used where no material was available",
00174                         /*  3 */ "Texture file not found",
00175                         /*  4 */ "Identical vertex indices found in the same faces -- faces ignored",
00176                         /*  5 */ "Faces with fewer than 3 vertices  -- faces ignored",
00177 
00178                         /*  6 */ "Can't open file",
00179                         /*  7 */ "Premature End of File. File truncated?",
00180                         /*  8 */ "Loading aborted by user",
00181                         /*  9 */ "No vertex found",
00182                         /* 10 */ "No face found",
00183                         /* 11 */ "Vertex statement with fewer than 3 coords",
00184                         /* 12 */ "Texture coords statement with fewer than 2 coords",
00185                         /* 13 */ "Vertex normal statement with fewer than 3 coords",
00186                         /* 14 */ "Bad vertex index in face",
00187                         /* 15 */ "Bad texture coords index in face",
00188                         /* 16 */ "Bad vertex normal index in face",
00189                         /* 17 */ "Quad faces with number of corners different from 4"
00190                     };
00191 
00192                     error >>= 1;
00193 
00194                     if( (error>=MAXST) || (error<0) ) return "Unknown error";
00195                     else return obj_error_msg[error];
00196                 }
00197 
00198                 // Helper functions that checks the range of indexes
00199                 // putting them in the correct range if less than zero (as in the obj style)
00200 
00201                 static bool GoodObjIndex(int &index, const int maxVal)
00202                 {
00203                     if (index > maxVal) return false;
00204                     if (index < 0)
00205                     {
00206                         index += maxVal+1;
00207                         if (index<0 || index > maxVal)  return false;
00208                     }
00209                     return true;
00210                 }
00211 
00212                 static int Open(OpenMeshType &mesh, const char *filename, int &loadmask, CallBackPos *cb=0)
00213                 {
00214                     Info oi;
00215                     oi.mask=0;
00216                     oi.cb=cb;
00217                     int ret=Open(mesh,filename,oi);
00218                     loadmask=oi.mask;
00219                     return ret;
00220                 }
00221 
00229                 static int Open( OpenMeshType &m, const char * filename, Info &oi)
00230                 {
00231                     int result = E_NOERROR;
00232 
00233                     m.Clear();
00234                     CallBackPos *cb = oi.cb;
00235 
00236                     // if LoadMask has not been called yet, we call it here
00237                     if (oi.mask == 0)
00238                         LoadMask(filename, oi);
00239 
00240                     const int inputMask = oi.mask;
00241                     Mask::ClampMask<OpenMeshType>(m,oi.mask);
00242 
00243                     if (oi.numVertices == 0)
00244                         return E_NO_VERTEX;
00245 
00246                     // Commented out this test. You should be allowed to load point clouds.
00247                     //if (oi.numFaces == 0)
00248                     //  return E_NO_FACE;
00249 
00250 
00251                     std::ifstream stream(filename);
00252                     if (stream.fail())
00253                     {
00254                         stream.close();
00255                         return E_CANTOPEN;
00256                     }
00257                     std::vector<Material>       materials;  // materials vector
00258                     std::vector<ObjTexCoord>    texCoords;  // texture coordinates
00259                     std::vector<CoordType>  normals;            // vertex normals
00260                     std::vector<ObjIndexedFace> indexedFaces;
00261                     std::vector< std::string > tokens;
00262                     std::string header;
00263 
00264                     short currentMaterialIdx = 0;                       // index of current material into materials vector
00265                     Color4b currentColor=Color4b::LightGray;    // we declare this outside code block since other
00266                     // triangles of this face will share the same color
00267 
00268                     Material defaultMaterial;                                   // default material: white
00269                     materials.push_back(defaultMaterial);
00270 
00271                     int numVertices  = 0;  // stores the number of vertices been read till now
00272                     int numEdges     = 0;  // stores the number of edges read till now
00273                     int numTriangles = 0;  // stores the number of faces been read till now
00274                     int numTexCoords = 0;  // stores the number of texture coordinates been read till now
00275                     int numVNormals      = 0;  // stores the number of vertex normals been read till now
00276 
00277                     int numVerticesPlusFaces = oi.numVertices + oi.numFaces;
00278                     int extraTriangles=0;
00279                     // vertices and faces allocation
00280                     VertexIterator vi = vcg::tri::Allocator<OpenMeshType>::AddVertices(m,oi.numVertices);
00281                     //FaceIterator   fi = Allocator<OpenMeshType>::AddFaces(m,oi.numFaces);
00282                     // edges found
00283                     std::vector<ObjEdge> ev;
00284                     std::vector<Color4b> vertexColorVector;
00285                     ObjIndexedFace      ff;
00286                     const char *loadingStr = "Loading";
00287                     while (!stream.eof())
00288                     {
00289                         tokens.clear();
00290                         TokenizeNextLine(stream, tokens,&vertexColorVector);
00291 
00292                         unsigned int numTokens = static_cast<unsigned int>(tokens.size());
00293                         if (numTokens > 0)
00294                         {
00295                             header.clear();
00296                             header = tokens[0];
00297 
00298                             // callback invocation, abort loading process if the call returns false
00299                             if ((cb !=NULL) && (((numTriangles + numVertices)%100)==0) && !(*cb)((100*(numTriangles + numVertices))/numVerticesPlusFaces, loadingStr))
00300                             {
00301                                 stream.close();
00302                                 return E_ABORTED;
00303                             }
00304                             if (header.compare("v")==0) // vertex
00305                             {
00306                                 loadingStr="Vertex Loading";
00307                                 if (numTokens < 4)
00308                                 {
00309                                     stream.close();
00310                                     return E_BAD_VERTEX_STATEMENT;
00311                                 }
00312                                 (*vi).P()[0] = (ScalarType) atof(tokens[1].c_str());
00313                                 (*vi).P()[1] = (ScalarType) atof(tokens[2].c_str());
00314                                 (*vi).P()[2] = (ScalarType) atof(tokens[3].c_str());
00315                                 ++numVertices;
00316 
00317                                 // assigning vertex color
00318                                 // ----------------------
00319                                 if (((oi.mask & vcg::tri::io::Mask::IOM_VERTCOLOR) != 0) && (HasPerVertexColor(m)))
00320                                 {
00321                                     if(numTokens>=7)
00322                                     {
00323                                         ScalarType rf(atof(tokens[4].c_str())), gf(atof(tokens[5].c_str())), bf(atof(tokens[6].c_str()));
00324                                         ScalarType scaling = (rf<=1 && gf<=1 && bf<=1) ? 255. : 1;
00325 
00326                                         unsigned char r                 = (unsigned char) ((ScalarType) atof(tokens[4].c_str()) * scaling);
00327                                         unsigned char g                 = (unsigned char) ((ScalarType) atof(tokens[5].c_str()) * scaling);
00328                                         unsigned char b                 = (unsigned char) ((ScalarType) atof(tokens[6].c_str()) * scaling);
00329                                         unsigned char alpha = (unsigned char) ((numTokens>=8 ? (ScalarType) atof(tokens[7].c_str()) : 1)  * scaling);
00330                                         (*vi).C() = Color4b(r, g, b, alpha);
00331                                     }
00332                                     else
00333                                     {
00334                                         (*vi).C() = currentColor;
00335                                     }
00336                                 }
00337 
00338                                 ++vi;  // move to next vertex iterator
00339                             }
00340                             else if (header.compare("vt")==0)   // vertex texture coords
00341                             {
00342                                 loadingStr="Vertex Texture Loading";
00343 
00344                                 if (numTokens < 3)
00345                                 {
00346                                     stream.close();
00347                                     return E_BAD_VERT_TEX_STATEMENT;
00348                                 }
00349                                 ObjTexCoord t;
00350                                 t.u = static_cast<float>(atof(tokens[1].c_str()));
00351                                 t.v = static_cast<float>(atof(tokens[2].c_str()));
00352                                 texCoords.push_back(t);
00353 
00354                                 numTexCoords++;
00355                             }
00356                             else if (header.compare("vn")==0)  // vertex normal
00357                             {
00358                                 loadingStr="Vertex Normal Loading";
00359 
00360                                 if (numTokens != 4)
00361                                 {
00362                                     stream.close();
00363                                     return E_BAD_VERT_NORMAL_STATEMENT;
00364                                 }
00365                                 CoordType n;
00366                                 n[0] = (ScalarType) atof(tokens[1].c_str());
00367                                 n[1] = (ScalarType) atof(tokens[2].c_str());
00368                                 n[2] = (ScalarType) atof(tokens[3].c_str());
00369                                 normals.push_back(n);
00370 
00371                                 numVNormals++;
00372                             }
00373                             else if ( header.compare("l")==0 )
00374                             {
00375                                 loadingStr = "Edge Loading";
00376 
00377                                 if (numTokens < 3)
00378                                 {
00379                                     result = E_LESS_THAN_3_VERT_IN_FACE; // TODO add proper/handling error code
00380                                     continue;
00381                                 }
00382 
00383                                 ObjEdge e = { (atoi(tokens[1].c_str()) - 1),
00384                                               (atoi(tokens[2].c_str()) - 1) };
00385                                 ev.push_back(e);
00386 
00387                                 numEdges++;
00388                             }
00389                             else if( (header.compare("f")==0) || (header.compare("q")==0) )  // face
00390                             {
00391                                 loadingStr="Face Loading";
00392 
00393                                 int vertexesPerFace = static_cast<int>(tokens.size()-1);
00394 
00395                                 bool QuadFlag = false; // QOBJ format by Silva et al for simply storing quadrangular meshes.
00396                                 if(header.compare("q")==0) {
00397                                     QuadFlag=true;
00398                                     if (vertexesPerFace != 4) {
00399                                         stream.close();
00400                                         return E_LESS_THAN_4_VERT_IN_QUAD;
00401                                     }
00402                                 }
00403 
00404 
00405                                 if (vertexesPerFace < 3) {
00406                                     // face with fewer than 3 vertices found: ignore this face
00407                                     extraTriangles--;
00408                                     result = E_LESS_THAN_3_VERT_IN_FACE;
00409                                     continue;
00410                                 }
00411 
00412 
00413                                 if( (vertexesPerFace>3) && OpenMeshType::FaceType::HasPolyInfo() )
00414                                 {
00415                                     //_BEGIN___ if  you are loading a GENERIC POLYGON mesh
00416                                     ff.set(vertexesPerFace);
00417                                     for(int i=0;i<vertexesPerFace;++i) { // remember index starts from 1 instead of 0
00418                                         SplitToken(tokens[i+1], ff.v[i], ff.n[i], ff.t[i], inputMask);
00419                                         if(QuadFlag) ff.v[i]++; // NOTE THAT THE STUPID QOBJ FORMAT IS ZERO INDEXED!!!!
00420                                     }
00421                                     if ( oi.mask & vcg::tri::io::Mask::IOM_WEDGTEXCOORD )
00422                                     {
00423                                         // verifying validity of texture coords indices
00424                                         for(int i=0;i<vertexesPerFace;i++)
00425                                             if(!GoodObjIndex(ff.t[i],oi.numTexCoords))
00426                                             {
00427                                                 stream.close();
00428                                                 return E_BAD_VERT_TEX_INDEX;
00429                                             }
00430                                         ff.tInd=materials[currentMaterialIdx].index;
00431                                     }
00432 
00433                                     // verifying validity of vertex indices
00434                                     std::vector<int> tmp = ff.v;
00435                                     std::sort(tmp.begin(),tmp.end());
00436                                     std::unique(tmp.begin(),tmp.end());
00437                                     if(tmp.size() != ff.v.size()) {
00438                                         result = E_VERTICES_WITH_SAME_IDX_IN_FACE;
00439                                         extraTriangles--;
00440                                         continue;
00441                                     }
00442 
00443                                     for(int i=0;i<vertexesPerFace;i++)
00444                                         if(!GoodObjIndex(ff.v[i],numVertices))
00445                                         {
00446                                             stream.close();
00447                                             return E_BAD_VERT_INDEX;
00448                                         }
00449 
00450                                     if(( oi.mask & vcg::tri::io::Mask::IOM_WEDGNORMAL ) ||
00451                                        ( oi.mask & vcg::tri::io::Mask::IOM_VERTNORMAL  ) )
00452                                     {
00453                                         // verifying validity of vertex normal indices
00454                                         for(int i=0;i<vertexesPerFace;i++)
00455                                             if(!GoodObjIndex(ff.n[i],numVNormals))
00456                                             {
00457                                                 stream.close();
00458                                                 return E_BAD_VERT_NORMAL_INDEX;
00459                                             }
00460                                     }
00461 
00462 
00463                                     if( oi.mask & vcg::tri::io::Mask::IOM_FACECOLOR) // assigning face color
00464                                         ff.c = currentColor;
00465 
00466                                     ++numTriangles;
00467                                     indexedFaces.push_back(ff);
00468 
00469                                     //_END  ___ if  you are loading a GENERIC POLYGON mesh
00470                                 }
00471                                 else
00472                                 {
00473                                     //_BEGIN___ if  you are loading a  TRIMESH mesh
00474                                     std::vector<std::vector<vcg::Point3f> > polygonVect(1); // it is a vector of polygon loops
00475                                     polygonVect[0].resize(vertexesPerFace);
00476                                     std::vector<int> indexVVect(vertexesPerFace);
00477                                     std::vector<int> indexNVect(vertexesPerFace);
00478                                     std::vector<int> indexTVect(vertexesPerFace);
00479                                     std::vector<int> indexTriangulatedVect;
00480 
00481                                     for(int pi=0;pi<vertexesPerFace;++pi)
00482                                     {
00483                                         SplitToken(tokens[pi+1], indexVVect[pi],indexNVect[pi],indexTVect[pi], inputMask);
00484                                         if(QuadFlag) indexVVect[pi]++; // NOTE THAT THE STUPID QOBJ FORMAT IS ZERO INDEXED!!!!
00485                                         GoodObjIndex(indexVVect[pi],numVertices);
00486                                         GoodObjIndex(indexTVect[pi],oi.numTexCoords);
00487                                         polygonVect[0][pi].Import(m.vert[indexVVect[pi]].cP());
00488                                     }
00489                                     if(vertexesPerFace>3)
00490                                        oi.mask |= Mask::IOM_BITPOLYGONAL;
00491 
00492                                     if(vertexesPerFace<5)
00493                                         FanTessellator(polygonVect, indexTriangulatedVect);
00494                                     else
00495                                     {
00496 #ifdef __gl_h_
00497                                         //qDebug("OK: using opengl tessellation for a polygon of %i verteces",vertexesPerFace);
00498                                         vcg::glu_tesselator::tesselate<vcg::Point3f>(polygonVect, indexTriangulatedVect);
00499                                         if(indexTriangulatedVect.size()==0)
00500                                           FanTessellator(polygonVect, indexTriangulatedVect);
00501 #else
00502                                         //qDebug("Warning: using fan tessellation for a polygon of %i verteces",vertexesPerFace);
00503                                         FanTessellator(polygonVect, indexTriangulatedVect);
00504 #endif
00505                                     }
00506                                     extraTriangles+=((indexTriangulatedVect.size()/3) -1);
00507 #ifdef QT_VERSION
00508                                     if( int(indexTriangulatedVect.size()/3) != vertexesPerFace-2)
00509                                     {
00510                                         qDebug("Warning there is a degenerate poligon of %i verteces that was triangulated into %i triangles",vertexesPerFace,int(indexTriangulatedVect.size()/3));
00511                                         for(size_t qq=0;qq<polygonVect[0].size();++qq)
00512                                             qDebug("      (%f %f %f)",polygonVect[0][qq][0],polygonVect[0][qq][1],polygonVect[0][qq][2]);
00513                                         for(size_t qq=0;qq<tokens.size();++qq) qDebug("<%s>",tokens[qq].c_str());
00514                                     }
00515 #endif
00516                                     //qDebug("Triangulated a face of %i vertexes into %i triangles",polygonVect[0].size(),indexTriangulatedVect.size());
00517 
00518                                     for(size_t pi=0;pi<indexTriangulatedVect.size();pi+=3)
00519                                     {
00520                                         ff.set(3);
00521                                         int locInd[3];
00522                                         for(int iii=0;iii<3;++iii)
00523                                         {
00524                                             locInd[iii]=indexTriangulatedVect[pi+iii];
00525                                             ff.v[iii]=indexVVect[ locInd[iii] ];
00526                                             ff.n[iii]=indexNVect[ locInd[iii] ];
00527 //                                                                                      qDebug("ff.n[iii]=indexNVect[ locInd[iii] ]; %i", ff.n[iii]);
00528                                             ff.t[iii]=indexTVect[ locInd[iii] ];
00529                                         }
00530 
00531                                         // Setting internal edges: only edges formed by consecutive edges are external.
00532                                         for(int iii=0;iii<3;++iii)
00533                                         {
00534                                             if( (locInd[iii]+1)%vertexesPerFace == locInd[(iii+1)%3]) ff.edge[iii]=false;
00535                                             else ff.edge[iii]=true;
00536                                         }
00537 
00538                                         if ( oi.mask & vcg::tri::io::Mask::IOM_WEDGTEXCOORD )
00539                                         { // verifying validity of texture coords indices
00540                                             bool invalid = false;
00541                                             for(int i=0;i<3;i++)
00542                                                 if(!GoodObjIndex(ff.t[i],oi.numTexCoords))
00543                                                 {
00544                                                     //return E_BAD_VERT_TEX_INDEX;
00545                                                     invalid = true;
00546                                                     break;
00547                                                 }
00548                                                 if (invalid) continue;
00549                                                 ff.tInd=materials[currentMaterialIdx].index;
00550                                         }
00551 
00552                                         // verifying validity of vertex indices
00553                                         if ((ff.v[0] == ff.v[1]) || (ff.v[0] == ff.v[2]) || (ff.v[1] == ff.v[2])) {
00554                                             result = E_VERTICES_WITH_SAME_IDX_IN_FACE;
00555                                             extraTriangles--;
00556                                             continue;
00557                                         }
00558 
00559                                         {
00560                                             bool invalid = false;
00561                                             for(int i=0;i<3;i++)
00562                                                 if(!GoodObjIndex(ff.v[i],numVertices))
00563                                                 {
00564                                                     //return E_BAD_VERT_INDEX;
00565                                                     invalid = true;
00566                                                     break;
00567                                                 }
00568                                             if (invalid) continue;
00569                                         }
00570 
00571                                         // assigning face normal
00572                                         if ( ( oi.mask & vcg::tri::io::Mask::IOM_WEDGNORMAL  ) ||
00573                                              ( oi.mask & vcg::tri::io::Mask::IOM_VERTNORMAL  ) )
00574                                         {   // verifying validity of vertex normal indices
00575                                             bool invalid = false;
00576                                             for(int i=0;i<3;i++)
00577                                                 if(!GoodObjIndex(ff.n[i],numVNormals))
00578                                                 {
00579                                                     //return E_BAD_VERT_NORMAL_INDEX;
00580                                                     invalid = true;
00581                                                     break;
00582                                                 }
00583                                                 if (invalid) continue;
00584                                         }
00585 
00586                                         // assigning face color
00587                                         if( oi.mask & vcg::tri::io::Mask::IOM_FACECOLOR) ff.c = currentColor;
00588 
00589                                         ++numTriangles;
00590                                         indexedFaces.push_back(ff);
00591                                     }
00592 
00593                                 }
00594                             }
00595                             else if ((header.compare("mtllib")==0) && (tokens.size() > 1))      // material library
00596                             {
00597                                 // obtain the name of the file containing materials library
00598                                 std::string materialFileName = tokens[1];
00599                                 if (!LoadMaterials( materialFileName.c_str(), materials, m.textures))
00600                                     result = E_MATERIAL_FILE_NOT_FOUND;
00601                             }
00602                             else if ((header.compare("usemtl")==0) && (tokens.size() > 1))      // material usage
00603                             {
00604                                 std::string materialName = tokens[1];
00605                                 bool found = false;
00606                                 unsigned i = 0;
00607                                 while (!found && (i < materials.size()))
00608                                 {
00609                                     std::string currentMaterialName = materials[i].materialName;
00610                                     if (currentMaterialName == materialName)
00611                                     {
00612                                         currentMaterialIdx = i;
00613                                         Material &material = materials[currentMaterialIdx];
00614                                         Point3f diffuseColor = material.Kd;
00615                                         unsigned char r                 = (unsigned char) (diffuseColor[0] * 255.0);
00616                                         unsigned char g                 = (unsigned char) (diffuseColor[1] * 255.0);
00617                                         unsigned char b                 = (unsigned char) (diffuseColor[2] * 255.0);
00618                                         unsigned char alpha = (unsigned char) (material.Tr  * 255.0);
00619                                         currentColor= Color4b(r, g, b, alpha);
00620                                         found = true;
00621                                     }
00622                                     ++i;
00623                                 }
00624 
00625                                 if (!found)
00626                                 {
00627                                     currentMaterialIdx = 0;
00628                                     result = E_MATERIAL_NOT_FOUND;
00629                                 }
00630                             }
00631                             // we simply ignore other situations
00632                         } // end for each line...
00633                     } // end while stream not eof
00634                     assert((numTriangles +numVertices) == numVerticesPlusFaces+extraTriangles);
00635                     vcg::tri::Allocator<OpenMeshType>::AddFaces(m,numTriangles);
00636 
00637                     // Add found edges
00638                     if (numEdges > 0)
00639                     {
00640                         vcg::tri::Allocator<OpenMeshType>::AddEdges(m,numEdges);
00641 
00642                         assert(m.edge.size() == size_t(m.en));
00643 
00644                         for(int i=0; i<numEdges; ++i)
00645                         {
00646                             ObjEdge &  e    = ev[i];
00647                             EdgeType & edge = m.edge[i];
00648 
00649                             assert(e.v0 >= 0 && size_t(e.v0) < m.vert.size() &&
00650                                    e.v1 >= 0 && size_t(e.v1) < m.vert.size());
00651                             // TODO add proper handling of bad indices
00652 
00653                             edge.V(0) = &(m.vert[e.v0]);
00654                             edge.V(1) = &(m.vert[e.v1]);
00655                         }
00656                     }
00657                     //-------------------------------------------------------------------------------
00658 
00659                     // Now the final passes:
00660                     // First Pass to convert indexes into pointers for face to vert/norm/tex references
00661                     for(int i=0; i<numTriangles; ++i)
00662                     {
00663                         assert(m.face.size() == size_t(m.fn));
00664                         m.face[i].Alloc(indexedFaces[i].v.size()); // it does not do anything if it is a trimesh
00665 
00666                         for(unsigned int j=0;j<indexedFaces[i].v.size();++j)
00667                         {
00668                             m.face[i].V(j) = &(m.vert[indexedFaces[i].v[j]]);
00669 
00670                             if (((oi.mask & vcg::tri::io::Mask::IOM_WEDGTEXCOORD) != 0) && (HasPerWedgeTexCoord(m)))
00671                             {
00672                                 ObjTexCoord t = texCoords[indexedFaces[i].t[j]];
00673                                 m.face[i].WT(j).u() = t.u;
00674                                 m.face[i].WT(j).v() = t.v;
00675                                 m.face[i].WT(j).n() = indexedFaces[i].tInd;
00676                             }
00677                             if ( oi.mask & vcg::tri::io::Mask::IOM_VERTTEXCOORD ) {
00678                                 ObjTexCoord t = texCoords[indexedFaces[i].t[j]];
00679                                 m.face[i].V(j)->T().u() = t.u;
00680                                 m.face[i].V(j)->T().v() = t.v;
00681                                 m.face[i].V(j)->T().n() = indexedFaces[i].tInd;
00682                             }
00683                             if ( oi.mask & vcg::tri::io::Mask::IOM_WEDGNORMAL )
00684                             {
00685                                 m.face[i].WN(j).Import(normals[indexedFaces[i].n[j]]);
00686                             }
00687 
00688                             if ( oi.mask & vcg::tri::io::Mask::IOM_VERTNORMAL )
00689                             {
00690 //                                                        qDebug("XXXXXX %i",indexedFaces[i].n[j]);
00691                                 m.face[i].V(j)->N().Import(normals[indexedFaces[i].n[j]]);
00692                             }
00693 
00694                             // set faux edge flags according to internals faces
00695                             if (indexedFaces[i].edge[j]) m.face[i].SetF(j);
00696                             else m.face[i].ClearF(j);
00697                         }
00698 
00699                         if (HasPerFaceNormal(m))
00700                         {
00701                             if (((oi.mask & vcg::tri::io::Mask::IOM_FACECOLOR) != 0) && (HasPerFaceColor(m)))
00702                             {
00703                                 m.face[i].C() = indexedFaces[i].c;
00704                             }
00705 
00706                             if (((oi.mask & vcg::tri::io::Mask::IOM_WEDGNORMAL) != 0) && (HasPerWedgeNormal(m)))
00707                             {
00708                                 // face normal is computed as an average of wedge normals
00709                                 m.face[i].N().Import(m.face[i].WN(0)+m.face[i].WN(1)+m.face[i].WN(2));
00710                             }
00711                             else
00712                             {
00713                                 m.face[i].N().Import(TriangleNormal(m.face[i]).Normalize());
00714                             }
00715                         }
00716                     }
00717                     // final pass to manage the ZBrush PerVertex Color that are managed into comments
00718                     if(vertexColorVector.size()>0)
00719                     {
00720                         //        if(vertexColorVector.size()!=m.vn){
00721                         //              qDebug("Warning Read %i vertices and %i vertex colors",m.vn,vertexColorVector.size());
00722                         //              qDebug("line count %i x 64 = %i",MRGBLineCount(), MRGBLineCount()*64);
00723                         //        }
00724                         for(int i=0;i<m.vn;++i)
00725                         {
00726                             m.vert[i].C()=vertexColorVector[i];
00727                         }
00728                     }
00729                     stream.close();
00730                     return result;
00731                 } // end of Open
00732 
00733 
00740                 inline static void TokenizeNextLine(std::ifstream &stream, std::vector< std::string > &tokens, std::vector<Color4b> *colVec)
00741                 {
00742                     if(stream.eof()) return;
00743                     std::string line;
00744                     do
00745                     {
00746                         std::getline(stream, line);
00747                         const size_t len = line.length();
00748                         if((len > 0) && colVec && line[0] == '#')
00749                         {
00750                             // The following MRGB block contains ZBrush Vertex Color (Polypaint)
00751                             // and masking output as 4 hexadecimal values per vertex. The vertex color format is MMRRGGBB with up to 64 entries per MRGB line.
00752                             if((len >= 5) && line[1] == 'M' && line[2] == 'R' && line[3] == 'G' && line[4] == 'B')
00753                             { // Parsing the polycolor of ZBrush
00754                                 MRGBLineCount()++;
00755                                 char buf[3]="00";
00756                                 Color4b cc(Color4b::Black);
00757                                 for(size_t i=6;(i+7)<len;i+=8)
00758                                 {
00759                                     for(size_t j=1;j<4;j++)
00760                                     {
00761                                         buf[0]=line[i+j*2+0];
00762                                         buf[1]=line[i+j*2+1];
00763                                         buf[2]=0;
00764                                         char *p;
00765                                         int val=strtoul(buf,&p,16);
00766                                         cc[j-1]= val;
00767                                     }
00768                                     colVec->push_back(cc);
00769                                 }
00770                             }
00771                         }
00772                     }
00773                     while (( line.length()==0 || line[0] == '#') && !stream.eof());  // skip comments and empty lines
00774 
00775                     if ( (line.length() == 0)||(line[0] == '#') )  // can be true only on last line of file
00776                         return;
00777 
00778                     size_t from         = 0;
00779                     size_t to                   = 0;
00780                     size_t length = line.size();
00781 
00782                     tokens.clear();
00783                     do
00784                     {
00785                         while (from!=length && (line[from]==' ' || line[from]=='\t' || line[from]=='\r') )
00786                             from++;
00787                         if(from!=length)
00788                         {
00789                             to = from+1;
00790                             while (to!=length && line[to]!=' ' && line[to] != '\t' && line[to]!='\r')
00791                                 to++;
00792                             tokens.push_back(line.substr(from, to-from).c_str());
00793                             from = to;
00794                         }
00795                     }
00796                     while (from<length);
00797                 } // end TokenizeNextLine
00798 
00799                 inline static void SplitToken(const std::string & token, int & vId, int & nId, int & tId, int mask)
00800                 {
00801                     static const char delimiter = '/';
00802 
00803                     vId = nId = tId = 0;
00804                     if (token.empty()) return;
00805 
00806                     size_t firstSep  = token.find_first_of(delimiter);
00807                     size_t secondSep = (firstSep == std::string::npos) ? (std::string::npos) : (token.find_first_of(delimiter, firstSep + 1));
00808 
00809                     const bool hasPosition = true;
00810                     const bool hasTexcoord = (firstSep  != std::string::npos) && ((firstSep + 1) < secondSep);
00811                     const bool hasNormal   = (secondSep != std::string::npos) || (mask & Mask::IOM_WEDGNORMAL) || (mask & Mask::IOM_VERTNORMAL);
00812 
00813                     if (hasPosition) vId = atoi(token.substr(0, firstSep).c_str()) - 1;
00814                     if (hasTexcoord) tId =               atoi(token.substr(firstSep + 1, secondSep - firstSep - 1).c_str()) - 1;
00815                     if (hasNormal)
00816                       nId = atoi(token.substr(secondSep + 1).c_str()) - 1;
00817 //                                      qDebug("%s -> %i %i %i",token.c_str(),vId,nId,tId);
00818                     /*
00819                     const std::string vStr = (hasPosition) ? (token.substr(0, firstSep))                            : ("0");
00820                     const std::string tStr = (hasTexcoord) ? (token.substr(firstSep + 1, secondSep - firstSep - 1)) : ("0");
00821                     const std::string nStr = (hasNormal)   ? (token.substr(secondSep + 1))                          : ("0");
00822 
00823                     if (!vStr.empty()) vId = atoi(vStr.c_str()) - 1;
00824                     if (!tStr.empty()) tId = atoi(tStr.c_str()) - 1;
00825                     if (!nStr.empty()) nId = atoi(nStr.c_str()) - 1;
00826                     */
00827                 }
00828 
00829 #if 0
00830                 // This function takes a token and, according to the mask, it returns the indexes of the involved vertex, normal and texcoord indexes.
00831                 // Example. if the obj file has vertex texcoord (e.g. lines 'vt 0.444 0.5555')
00832                 // when parsing  a line like
00833                 // f 46/303 619/325 624/326 623/327
00834                 // if in the mask you have specified to read wedge tex coord
00835                 // for the first token it will return inside vId and tId the corresponding indexes 46 and 303 )
00836                 inline static void SplitToken(std::string token, int &vId, int &nId, int &tId, int mask)
00837                 {
00838                     std::string vertex;
00839                     std::string texcoord;
00840                     std::string normal;
00841 
00842                     if( ( mask & Mask::IOM_WEDGTEXCOORD ) && (mask & Mask::IOM_WEDGNORMAL) )   SplitVVTVNToken(token, vertex, texcoord, normal);
00843                     if(!( mask & Mask::IOM_WEDGTEXCOORD ) && (mask & Mask::IOM_WEDGNORMAL) )   SplitVVNToken(token, vertex, normal);
00844                     if( ( mask & Mask::IOM_WEDGTEXCOORD ) &&!(mask & Mask::IOM_WEDGNORMAL) )   SplitVVTToken(token, vertex, texcoord);
00845                     if(!( mask & Mask::IOM_WEDGTEXCOORD ) &&!(mask & Mask::IOM_WEDGNORMAL) )   SplitVToken(token, vertex);
00846 
00847                     vId = atoi(vertex.c_str()) - 1;
00848                     if(mask & Mask::IOM_WEDGTEXCOORD) tId = atoi(texcoord.c_str()) - 1;
00849                     if(mask & Mask::IOM_WEDGNORMAL)   nId = atoi(normal.c_str())   - 1;
00850                 }
00851 
00852                 inline static void SplitVToken(std::string token, std::string &vertex)
00853                 {
00854                     vertex = token;
00855                 }
00856 
00857                 inline static void SplitVVTToken(std::string token, std::string &vertex, std::string &texcoord)
00858                 {
00859                     vertex.clear();
00860                     texcoord.clear();
00861 
00862                     size_t from         = 0;
00863                     size_t to                   = 0;
00864                     size_t length = token.size();
00865 
00866                     if(from!=length)
00867                     {
00868                         char c = token[from];
00869                         vertex.push_back(c);
00870 
00871                         to = from+1;
00872                         while (to<length && ((c = token[to]) !='/'))
00873                         {
00874                             vertex.push_back(c);
00875                             ++to;
00876                         }
00877                         ++to;
00878                         while (to<length && ((c = token[to]) !=' '))
00879                         {
00880                             texcoord.push_back(c);
00881                             ++to;
00882                         }
00883                     }
00884                 }       // end of SplitVVTToken
00885 
00886                 inline static void SplitVVNToken(std::string token, std::string &vertex, std::string &normal)
00887                 {
00888                     vertex.clear();
00889                     normal.clear();
00890 
00891                     size_t from         = 0;
00892                     size_t to                   = 0;
00893                     size_t length = token.size();
00894 
00895                     if(from!=length)
00896                     {
00897                         char c = token[from];
00898                         vertex.push_back(c);
00899 
00900                         to = from+1;
00901                         while (to!=length && ((c = token[to]) !='/'))
00902                         {
00903                             vertex.push_back(c);
00904                             ++to;
00905                         }
00906                         ++to;
00907                         ++to;  // should be the second '/'
00908                         while (to!=length && ((c = token[to]) !=' '))
00909                         {
00910                             normal.push_back(c);
00911                             ++to;
00912                         }
00913                     }
00914                 }       // end of SplitVVNToken
00915 
00916                 inline static void SplitVVTVNToken(std::string token, std::string &vertex, std::string &texcoord, std::string &normal)
00917                 {
00918                     vertex.clear();
00919                     texcoord.clear();
00920                     normal.clear();
00921 
00922                     size_t from         = 0;
00923                     size_t to                   = 0;
00924                     size_t length = token.size();
00925 
00926                     if(from!=length)
00927                     {
00928                         char c = token[from];
00929                         vertex.push_back(c);
00930 
00931                         to = from+1;
00932                         while (to!=length && ((c = token[to]) !='/'))
00933                         {
00934                             vertex.push_back(c);
00935                             ++to;
00936                         }
00937                         ++to;
00938                         while (to!=length && ((c = token[to]) !='/'))
00939                         {
00940                             texcoord.push_back(c);
00941                             ++to;
00942                         }
00943                         ++to;
00944                         while (to!=length && ((c = token[to]) !=' '))
00945                         {
00946                             normal.push_back(c);
00947                             ++to;
00948                         }
00949                     }
00950                 }       // end of SplitVVTVNToken
00951 #endif
00952 
00960                 static bool LoadMask(const char * filename, Info &oi)
00961                 {
00962 
00963                     std::ifstream stream(filename);
00964                     if (stream.fail())
00965                     {
00966                         stream.close();
00967                         return false;
00968                     }
00969                     // obtain length of file:
00970                     stream.seekg (0, std::ios::end);
00971                     int length = stream.tellg();
00972                     stream.seekg (0, std::ios::beg);
00973 
00974                     if (length == 0) return false;
00975 
00976                     bool bHasPerFaceColor               = false;
00977                     bool bHasNormals                            = false;
00978                     bool bHasPerVertexColor = false;
00979 
00980                     oi.numVertices=0;
00981                     oi.numEdges=0;
00982                     oi.numFaces=0;
00983                     oi.numTexCoords=0;
00984                     oi.numNormals=0;
00985                     int lineCount=0;
00986                     int totRead=0;
00987                     std::string line;
00988                     while (!stream.eof())
00989                     {
00990                         lineCount++;
00991                         std::getline(stream, line);
00992                         totRead+=line.size();
00993                         if(oi.cb && (lineCount%1000)==0)
00994                             (*oi.cb)( (int)(100.0*(float(totRead))/float(length)), "Loading mask...");
00995                         if(line.size()>2)
00996                         {
00997                             if(line[0]=='v')
00998                             {
00999                                 if(line[1]==' ')
01000                                 {
01001                                     oi.numVertices++;
01002                                     if(line.size()>=7)
01003                                         bHasPerVertexColor = true;
01004                                 }
01005                                 if(line[1]=='t') oi.numTexCoords++;
01006                                 if(line[1]=='n') {
01007                                     oi.numNormals ++;
01008                                     bHasNormals = true;
01009                                 }
01010                             }
01011                             else {
01012                                 if((line[0]=='f') || (line[0]=='q')) oi.numFaces++;
01013                                 else
01014                                     if (line[0]=='l') oi.numEdges++;
01015                                 else
01016                                     if(line[0]=='u' && line[1]=='s') bHasPerFaceColor = true; // there is a usematerial so add per face color
01017                             }
01018                         }
01019                     }
01020                     oi.mask = 0;
01021                     if (oi.numTexCoords)
01022                     {
01023                         if (oi.numTexCoords==oi.numVertices)
01024                             oi.mask |= vcg::tri::io::Mask::IOM_VERTTEXCOORD;
01025 
01026                         oi.mask |= vcg::tri::io::Mask::IOM_WEDGTEXCOORD;
01027                         // Usually if you have tex coords you also have materials
01028                         oi.mask |= vcg::tri::io::Mask::IOM_FACECOLOR;
01029                     }
01030                     if(bHasPerFaceColor)                oi.mask |= vcg::tri::io::Mask::IOM_FACECOLOR;
01031                     if(bHasPerVertexColor)      oi.mask |= vcg::tri::io::Mask::IOM_VERTCOLOR;
01032                     if (bHasNormals) {
01033                         if (oi.numNormals == oi.numVertices)
01034                             oi.mask |= vcg::tri::io::Mask::IOM_VERTNORMAL;
01035                         else
01036                             oi.mask |= vcg::tri::io::Mask::IOM_WEDGNORMAL;
01037                     }
01038                     if (oi.numEdges)
01039                         oi.mask |= vcg::tri::io::Mask::IOM_EDGEINDEX;
01040 
01041                     stream.close();
01042 
01043                     return true;
01044                 }
01045 
01046                 static bool LoadMask(const char * filename, int &mask)
01047                 {
01048                     Info oi;
01049                     bool ret=LoadMask(filename, oi);
01050                     mask= oi.mask;
01051                     return ret;
01052                 }
01053 
01054                 static bool LoadMaterials(const char * filename, std::vector<Material> &materials, std::vector<std::string> &textures)
01055                 {
01056                     // assumes we are in the right directory
01057 
01058                     std::ifstream stream(filename);
01059                     if (stream.fail())
01060                         return false;
01061 
01062                     std::vector< std::string > tokens;
01063                     std::string header;
01064 
01065                     materials.clear();
01066                     Material currentMaterial;
01067                     currentMaterial.index = (unsigned int)(-1);
01068 
01069                     bool first = true;
01070                     while (!stream.eof())
01071                     {
01072                         tokens.clear();
01073                         TokenizeNextLine(stream, tokens,0);
01074 
01075                         if (tokens.size() > 0)
01076                         {
01077                             header.clear();
01078                             header = tokens[0];
01079 
01080                             if (header.compare("newmtl")==0)
01081                             {
01082                                 if (!first)
01083                                 {
01084                                     materials.push_back(currentMaterial);
01085                                     currentMaterial = Material();
01086                                     currentMaterial.index = (unsigned int)(-1);
01087                                 }
01088                                 else
01089                                     first = false;
01090                                 //strcpy(currentMaterial.name, tokens[1].c_str());
01091                                 if(tokens.size() < 2)
01092                                     return false;
01093                                 currentMaterial.materialName=tokens[1];
01094                             }
01095                             else if (header.compare("Ka")==0)
01096                             {
01097                                 if (tokens.size() < 4)
01098                                     return false;
01099                                 float r = (float) atof(tokens[1].c_str());
01100                                 float g = (float) atof(tokens[2].c_str());
01101                                 float b = (float) atof(tokens[3].c_str());
01102 
01103                                 currentMaterial.Ka = Point3f(r, g, b);
01104                             }
01105                             else if (header.compare("Kd")==0)
01106                             {
01107                                 if (tokens.size() < 4)
01108                                     return false;
01109                                 float r = (float) atof(tokens[1].c_str());
01110                                 float g = (float) atof(tokens[2].c_str());
01111                                 float b = (float) atof(tokens[3].c_str());
01112 
01113                                 currentMaterial.Kd = Point3f(r, g, b);
01114                             }
01115                             else if (header.compare("Ks")==0)
01116                             {
01117                                 if (tokens.size() < 4)
01118                                     return false;
01119                                 float r = (float) atof(tokens[1].c_str());
01120                                 float g = (float) atof(tokens[2].c_str());
01121                                 float b = (float) atof(tokens[3].c_str());
01122 
01123                                 currentMaterial.Ks = Point3f(r, g, b);
01124                             }
01125                             else if (   (header.compare("d")==0) ||
01126                                 (header.compare("Tr")==0)       )       // alpha
01127                             {
01128                                 if (tokens.size() < 2)
01129                                     return false;
01130                                 currentMaterial.Tr = (float) atof(tokens[1].c_str());
01131                             }
01132                             else if (header.compare("Ns")==0)  // shininess
01133                             {
01134                                 if (tokens.size() < 2)
01135                                     return false;
01136                                 currentMaterial.Ns = float(atoi(tokens[1].c_str()));
01137                             }
01138                             else if (header.compare("illum")==0)        // specular illumination on/off
01139                             {
01140                                 if (tokens.size() < 2)
01141                                     return false;
01142                                 int illumination = atoi(tokens[1].c_str());
01143                                 //currentMaterial.bSpecular = (illumination == 2);
01144                                 currentMaterial.illum = illumination;
01145                             }
01146                             else if( (header.compare("map_Kd")==0)      || (header.compare("map_Ka")==0) ) // texture name
01147                             {
01148                                 if (tokens.size() < 2)
01149                                     return false;
01150                                 std::string textureName = tokens[1];
01151                                 //strcpy(currentMaterial.textureFileName, textureName.c_str());
01152                                 currentMaterial.map_Kd=textureName;
01153 
01154                                 // adding texture name into textures vector (if not already present)
01155                                 // avoid adding the same name twice
01156                                 bool found = false;
01157                                 unsigned int size = static_cast<unsigned int>(textures.size());
01158                                 unsigned j = 0;
01159                                 while (!found && (j < size))
01160                                 {
01161                                     if (textureName.compare(textures[j])==0)
01162                                     {
01163                                         currentMaterial.index = (int)j;
01164                                         found = true;
01165                                     }
01166                                     ++j;
01167                                 }
01168                                 if (!found)
01169                                 {
01170                                     textures.push_back(textureName);
01171                                     currentMaterial.index = (int)size;
01172                                 }
01173                             }
01174                             // we simply ignore other situations
01175                         }
01176                     }
01177                     materials.push_back(currentMaterial);  // add last read material
01178 
01179                     stream.close();
01180 
01181                     return true;
01182                 }
01183 
01184             }; // end class
01185         } // end Namespace tri
01186     } // end Namespace io
01187 } // end Namespace vcg
01188 
01189 #endif  // ndef __VCGLIB_IMPORT_OBJ


shape_reconstruction
Author(s): Roberto Martín-Martín
autogenerated on Sat Jun 8 2019 18:31:57