Main Page | Modules | Namespace List | Class Hierarchy | Alphabetical List | Class List | 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 Oct 18 14:17:39 2004 for Sh by doxygen 1.3.7