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

Generated on Wed Jun 15 18:12:41 2005 for Sh by  doxygen 1.4.3-20050530