Main Page | Modules | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

ShObjMesh.cpp

00001 // Sh: A GPU metaprogramming language.
00002 //
00003 // Copyright (c) 2003 University of Waterloo Computer Graphics Laboratory
00004 // Project administrator: Michael D. McCool
00005 // Authors: Zheng Qin, Stefanus Du Toit, Kevin Moule, Tiberiu S. Popa,
00006 //          Michael D. McCool
00007 // 
00008 // This software is provided 'as-is', without any express or implied
00009 // warranty. In no event will the authors be held liable for any damages
00010 // arising from the use of this software.
00011 // 
00012 // Permission is granted to anyone to use this software for any purpose,
00013 // including commercial applications, and to alter it and redistribute it
00014 // freely, subject to the following restrictions:
00015 // 
00016 // 1. The origin of this software must not be misrepresented; you must
00017 // not claim that you wrote the original software. If you use this
00018 // software in a product, an acknowledgment in the product documentation
00019 // would be appreciated but is not required.
00020 // 
00021 // 2. Altered source versions must be plainly marked as such, and must
00022 // not be misrepresented as being the original software.
00023 // 
00024 // 3. This notice may not be removed or altered from any source
00025 // distribution.
00027 #include <iostream>
00028 #include <iomanip>
00029 #include <sstream>
00030 #include <cctype>
00031 #include "ShObjMesh.hpp"
00032 
00033 // ShObjMesh converted from old ShObjFile
00034 
00035 namespace ShUtil {
00036 
00037 struct Triple {
00038     int idx[3];
00039 
00040     // Reads OBJ indices for a face vertex from in.
00041     // Keeps '\n' if it hits end of line.
00042     // If nothing is read, then idx[0] = 0 (OBJ indices start from 1)
00043     Triple(std::istream &in) {
00044       idx[0] = idx[1] = idx[2] = 0;
00045       while(in.peek() != '\n' && isspace(in.peek())) in.ignore();
00046 
00047       // each vertex spec must not have spaces (just copying what the old ShObjFile did...)
00048       in >> std::noskipws;  
00049       for(int i = 0; i < 3;) {
00050         in >> idx[i];
00051         if( !in ) {
00052           in.clear();
00053           if(in.peek() == '/') {
00054             ++i;
00055             in.ignore(); 
00056           }else {
00057             return;
00058           }
00059         } 
00060       }
00061       in >> std::skipws;
00062     }
00063 
00064     int operator[](int i) { return idx[i]; }
00065 };
00066 
00067 typedef std::vector<Triple> ShObjIndexedFace;
00068 
00069 ShObjVertex::ShObjVertex(const ShPoint3f &p)
00070   : pos(p) {}
00071 
00072 ShObjMesh::ShObjMesh() {
00073 }
00074 
00075 ShObjMesh::ShObjMesh(std::istream &in) {
00076   readObj(in);
00077 }
00078 
00079 std::istream& ShObjMesh::readObj(std::istream &in) {
00080   char ch = 0;
00081   typedef std::vector<Vertex*> VertexVec;
00082   typedef std::vector<ShTexCoord2f> TexCoordVec;
00083   typedef std::vector<ShNormal3f> NormalVec;
00084   typedef std::vector<ShVector3f> TangentVec;
00085   typedef std::vector<ShObjIndexedFace> FaceVec;
00086 
00087   VertexVec vertexVec;
00088   TexCoordVec tcVec;
00089   NormalVec normVec;
00090   TangentVec tangentVec;
00091   FaceVec faceVec;
00092 
00093   // remove old mesh
00094   clear();
00095 
00096   // read in verts,tangents,normals, etc. first 
00097   while (in) {
00098     in >> std::ws >> ch;
00099     if (!in) break; // TODO: Check for error conditions.
00100     switch (ch) {
00101       case 'v': {
00102         ch = in.get();
00103         switch (ch) {
00104           case ' ': { // vertex
00105             float x, y, z;
00106             in >> x >> y >> z;
00107             vertexVec.push_back(new Vertex(ShPoint3f(x, y, z)));
00108             break;
00109           }
00110           case 't': { // Texture coordinate
00111             float u, v;
00112             in >> u >> v;
00113             tcVec.push_back(ShTexCoord2f(u, v));
00114             break;
00115           }
00116           case 'n': {
00117             // Normal
00118             float x, y, z;
00119             in >> x >> y >> z;
00120             normVec.push_back(ShNormal3f(x, y, z));
00121             break;
00122           }
00123         }
00124         break;
00125       }
00126       case 'r': { // Tangent
00127         float x, y, z;
00128         in >> x >> y >> z;
00129         tangentVec.push_back(ShVector3f(x, y, z));
00130         break;
00131       }
00132       case 'f': { // Face
00133         ShObjIndexedFace f;
00134         for(;;) {
00135           Triple triple(in);
00136           if( triple[0] == 0 ) break; 
00137           f.push_back(triple);
00138         }
00139         faceVec.push_back(f);
00140         break;
00141       }
00142       case '#': // Comment
00143       case '$': // Apparently this is also used for comments sometimes
00144       case 'l': // Line, ignore
00145       case 'p': // Point, ignore
00146       case 'm': // material library, ignore
00147       case 'g': // group, ignore
00148       case 's': // smoothing group, ignore
00149       case 'u': // material, ignore
00150       default: // anything else we ignore as well
00151         break;
00152     }
00153     while (in && in.get() != '\n') ; // Ignore rest of line
00154   }
00155 
00156   // convert faceVec to mesh
00157   for(std::size_t i = 0; i < faceVec.size(); ++i) {
00158     VertexList vl;
00159     for(ShObjIndexedFace::iterator I = faceVec[i].begin(); I != faceVec[i].end(); ++I) {
00160       int vi = (*I)[0] - 1;
00161       if( vi == -1 || vi >= (int)vertexVec.size() ) {
00162         std::ostringstream os;
00163         os << "Invalid vertex index " << vi << " in OBJ file.";
00164         shError( ShException(os.str()));
00165       }
00166       vl.push_back(vertexVec[vi]);
00167     }
00168     Face* face = addFace(vl);
00169 
00170     // go through face again and set up edge properties
00171     Edge* edge = face->edge;
00172     for(ShObjIndexedFace::iterator I = faceVec[i].begin(); I != faceVec[i].end(); ++I, edge = edge->next) {
00173       int tci = (*I)[1] - 1;
00174       int ni = (*I)[2] - 1; 
00175 
00176       if( tci != -1 ) {
00177         if( tci >= (int)tcVec.size() ) {
00178           std::ostringstream os;
00179           os << "Invalid texcoord index " << tci << " in OBJ file.";
00180           shError( ShException(os.str()));
00181         }
00182         edge->texcoord = tcVec[tci]; 
00183       } else edge->texcoord *= 0.0f;
00184 
00185       if( ni != -1 ) {
00186         if( ni >= (int)normVec.size() ) {
00187           std::ostringstream os;
00188           os << "Invalid normal index " << ni << " in OBJ file.";
00189           shError( ShException(os.str()));
00190         }
00191         edge->normal = normVec[ni];
00192 
00193         if( ni >= (int)tangentVec.size() ) {
00194           edge->tangent *= 0.0f;
00195         } else {
00196           edge->tangent = tangentVec[ni];
00197         }
00198       }
00199     }
00200   }
00201 
00202   earTriangulate();
00203   generateFaceNormals();
00204 
00205   int badNorms = generateVertexNormals();
00206   if(badNorms > 0) SH_DEBUG_WARN("OBJ file has " << badNorms << " vertices without normals"); 
00207 
00208   int badTangents = generateTangents();
00209   if(badTangents > 0) SH_DEBUG_WARN("OBJ file has " << badTangents << " vertices without tangents"); 
00210 
00211   int badTexCoords = generateSphericalTexCoords();
00212   if(badTexCoords > 0) SH_DEBUG_WARN("OBJ file has " << badTexCoords << " vertices without texture coordinates.");
00213 
00214   // TODO flip faces that have vert normals not matching the face normal
00215   return in;
00216 }
00217 
00218 void ShObjMesh::generateFaceNormals() {
00219   for(FaceSet::iterator I = faces.begin(); I != faces.end(); ++I) {
00220     Face& face = **I;
00221     Edge& e01 = *(face.edge);
00222     Edge& e02 = *(e01.next);
00223     ShVector3f v01 = e01.end->pos - e01.start->pos;
00224     ShVector3f v02 = e02.end->pos - e02.start->pos; 
00225     face.normal = cross(v01, v02);  
00226   }
00227 }
00228 
00229 int ShObjMesh::generateVertexNormals(bool force) {
00230   typedef std::map<Vertex*, ShPoint3f> NormalSumMap;
00231   typedef std::map<Vertex*, int> NormalSumCount;
00232   NormalSumMap nsm;
00233   NormalSumCount nscount;
00234   for(EdgeSet::iterator I = edges.begin(); I != edges.end(); ++I) {
00235     Edge &e = **I;
00236     if( force || dot(e.normal, e.normal).getValue(0) == 0 ) { 
00237       nsm[e.start] = ShConstAttrib3f(0.0f, 0.0f, 0.0f);
00238       nscount[e.start] = 0;
00239     }
00240   }
00241   if( nsm.empty() ) return 0;
00242 
00243   for(EdgeSet::iterator I = edges.begin(); I != edges.end(); ++I) {
00244     Vertex *v = (*I)->start;
00245     if( nsm.count(v) > 0 ) {
00246       nsm[v] += (*I)->face->normal; 
00247       nscount[v]++;
00248     }
00249   }
00250 
00251   for(EdgeSet::iterator I = edges.begin(); I != edges.end(); ++I) {
00252     Vertex *v = (*I)->start;
00253     if( nsm.count(v) > 0 ) {
00254       (*I)->normal = nsm[v] / (float)nscount[v];
00255     }
00256   }
00257   return nsm.size();
00258 }
00259 
00260 int ShObjMesh::generateTangents(bool force) {
00261   int changed = 0;
00262   for(EdgeSet::iterator I = edges.begin(); I != edges.end(); ++I) {
00263     Edge &e = **I;
00264     if( force || dot(e.tangent, e.tangent).getValue(0)  == 0) {
00265       e.tangent = cross(e.normal, ShVector3f(0.0f, 1.0f, 0.0f));
00266       changed++;
00267     }
00268   }
00269   return changed; 
00270 }
00271 
00272 int ShObjMesh::generateSphericalTexCoords(bool force) {
00273   if( !force ) {
00274     // If some vertex has texcoords, don't mess with them
00275     for(EdgeSet::iterator I = edges.begin(); I != edges.end(); ++I) {
00276       if( dot((*I)->texcoord, (*I)->texcoord).getValue(0) != 0) {
00277         return 0;
00278       }
00279     }
00280   }
00281 
00282   // Find center and generate texture coordinates 
00283   int changed = 0;
00284   ShPoint3f center;
00285   for(VertexSet::iterator I = verts.begin(); I != verts.end(); ++I) {
00286     center += (*I)->pos;
00287   }
00288   center *= 1.0f / verts.size();
00289 
00290   for(EdgeSet::iterator I = edges.begin(); I != edges.end(); ++I) {
00291     Edge &e = **I;
00292     if( force || dot(e.texcoord, e.texcoord).getValue(0) == 0) {
00293       ShVector3f cv = normalize(e.start->pos - center);
00294       e.texcoord(0) = atan2(cv.getValue(2), cv.getValue(0));
00295       e.texcoord(1) = acos(cv.getValue(1));
00296       changed++; 
00297     }
00298   }
00299   return changed; 
00300 }
00301 
00302 void ShObjMesh::normalizeNormals() {
00303   for(EdgeSet::iterator I = edges.begin(); I != edges.end(); ++I) {
00304     (*I)->normal = normalize((*I)->normal);
00305   }
00306 
00307   for(FaceSet::iterator J = faces.begin(); J != faces.end(); ++J) {
00308     (*J)->normal = normalize((*J)->normal);
00309   }
00310 }
00311 
00312 struct ObjVertLess {
00313  static const float EPS;
00314 
00315  inline bool operator()( const ShObjVertex *a, const ShObjVertex *b ) const {
00316    float aval[3], bval[3];
00317    a->pos.getValues(aval); b->pos.getValues(bval);
00318 
00319    return (aval[0] < bval[0] - EPS) || ( 
00320           (aval[0] < bval[0] + EPS) && ( 
00321           (aval[1] < bval[1] - EPS) || ( 
00322           (aval[1] < bval[1] + EPS) && 
00323           (aval[2] < bval[2] - EPS))));
00324  }
00325 };
00326 
00327 const float ObjVertLess::EPS = 1e-5;
00328 
00329 void ShObjMesh::consolidateVertices() {
00330   mergeVertices<ObjVertLess>();
00331 }
00332 
00333 
00334 std::istream& operator>>(std::istream &in, ShObjMesh &mesh) {
00335   return mesh.readObj(in); 
00336 }
00337 
00338 }

Generated on Mon Jan 24 18:36:34 2005 for Sh by  doxygen 1.4.1