/* * wavefront.cpp * Mark Colbert * ma434187@pegasus.cc.ucf.edu * * Jan Kautz: Fixed bug for nVerticesTotal size not being set. * * Carsten Dachsbacher, bug fixed: for obj-files with unused vertices, we need to pass * the complete vertex list (=> nVerticesTotal) * * The following will parse triangulated obj files that can eaisly be exported from * lightwave, maya, or 3d studio max. * * TODO: The largest problem about this program is that if there are a different * number of normal verticies or UV verticies than point verticies, which there usually are, the * program will just create as many verticies as there are in the index to * easily ensure that every point has the proper UV, normal, and point vertex * information. The fix is to just use a more efficient methodology of combining * all the data. * * TODO: The other problem about this parser is that it only works with triangulated data. * In other words, if you give it an object that is anything but triangles, it will * only read the first 3 vertecies of a face and you will get one very odd looking * model. Thus, it would be nice to have some sort of triangulation algorithm in * this program to allow any generic OBJ file to be input for the plugin. * */ #include "shape.h" #include "paramset.h" #include "dynload.h" // line buffer size determines at compile time how large the input // buffer should be for the file input lines #define LINE_BUFFER_SIZE 1024 class Wavefront : public Shape { public: Wavefront(const char* filename, const Transform &o2w, bool reverseOrientation); ~Wavefront() { if (vertexIndex) delete[] vertexIndex; if (p) delete[] p; if (n) delete[] n; if (uvs) delete[] uvs; } BBox ObjectBound() const; BBox WorldBound() const; bool CanIntersect() const { return false; } void Refine(vector > &refined) const; private: void MergeIndicies(vector &points, vector &normals, vector &uvVec, vector &vIndex, vector &normalIndex, vector &uvIndex); int ntris, nverts; int nVerticesTotal; int *vertexIndex; Point *p; Normal *n; float *uvs; }; // GETNUM just gets the next number from a line of input in an OBJ file #ifndef GETNUM #define GETNUM(lineBuffer, numBuffer, lindex, nindex, tval) \ nindex=0;\ while ((lineBuffer[lindex] == ' ') || lineBuffer[lindex] == '/') lindex++;\ while ((lineBuffer[lindex] != ' ') && (lineBuffer[lindex] != '/') && \ (lineBuffer[lindex] != '\0') && (lineBuffer[lindex] != '\n') && (lindex != LINE_BUFFER_SIZE)) { \ numBuffer[nindex] = lineBuffer[lindex]; \ nindex++; \ lindex++; \ } \ numBuffer[nindex] = '\0'; \ tval = atoi(numBuffer); #endif // constructor / parser Wavefront::Wavefront(const char* filename, const Transform &o2w, bool reverseOrientation) : Shape(o2w, reverseOrientation), vertexIndex(NULL), p(NULL), n(NULL), uvs(NULL) { FILE* fin; fin = fopen(filename, "r"); if (!fin) return; // temporary input buffers vector points; vector verts; vector normalIndex; vector uvIndex; vector file_normals; vector file_uvvector; Point ptmp; Normal ntmp; float uv1, uv2; char lineBuffer[LINE_BUFFER_SIZE]; char numBuffer[32]; int lindex=0; int nindex=0; int ival, uvval, nval; ntris=0; // parse the data in while (fgets(lineBuffer, LINE_BUFFER_SIZE, fin)) { switch (lineBuffer[0]) { case 'v': // case vertex information if (lineBuffer[1] == ' ') { // regular vertex point sscanf(&lineBuffer[2], "%f %f %f", &ptmp.x, &ptmp.y, &ptmp.z); points.push_back(ptmp); } else if (lineBuffer[1] == 't') { // texture coordinates sscanf(&lineBuffer[3], "%f %f", &uv1, &uv2); file_uvvector.push_back(uv1); file_uvvector.push_back(uv2); } else if (lineBuffer[1] == 'n') { // normal vector sscanf(&lineBuffer[2], "%f %f %f", &ntmp.x, &ntmp.y, &ntmp.z); file_normals.push_back(ntmp); } break; case 'f': // case face information lindex = 2; ntris++; for (int i=0; i < 3; i++) { GETNUM(lineBuffer, numBuffer, lindex, nindex, ival) // obj files go from 1..n, this just allows me to access the memory // directly by droping the index value to 0...(n-1) ival--; verts.push_back(ival); if (lineBuffer[lindex] == '/') { lindex++; GETNUM(lineBuffer, numBuffer, lindex, nindex, uvval) uvIndex.push_back(uvval-1); } if (lineBuffer[lindex] == '/') { lindex++; GETNUM(lineBuffer, numBuffer, lindex, nindex, nval) normalIndex.push_back(nval-1); } lindex++; } break; case 'g': // not really caring about faces or materials now // so just making life easier, I'll ignoring it break; } } fclose(fin); // merge everything back into one index array instead of multiple arrays MergeIndicies(points, file_normals, file_uvvector, verts, normalIndex, uvIndex); points.clear(); file_normals.clear(); file_uvvector.clear(); verts.clear(); normalIndex.clear(); uvIndex.clear(); if (n) std::cout << "Used normals" << std::endl; if (uvs) std::cout << "Used texture coords" << std::endl; } void Wavefront::MergeIndicies(vector &points, vector &normals, vector &uvVec, vector &vIndex, vector &normalIndex, vector &uvIndex) { bool useNormals = !normals.empty(); bool useUVs = !uvVec.empty(); if (!useNormals && !useUVs) { std::cout << "Copying points" << std::endl; // just copy the points into the array nverts = vIndex.size(); p = new Point[points.size()]; nVerticesTotal = points.size(); for (unsigned int i=0; i < points.size(); i++) p[i] = points[i]; vertexIndex = new int[nverts]; for (int i=0; i < nverts; i++) vertexIndex[i] = vIndex[i]; return; } // assumes that vertexIndex = normalIndex = uvIndex nVerticesTotal = nverts = vIndex.size(); // FIX: Dec, 3 vertexIndex = new int[nverts]; p = new Point[nverts]; if (useNormals) n = new Normal[nverts]; if (useUVs) uvs = new float[nverts*2]; for (int i=0; i < nverts; i++) { p[i] = points[vIndex[i]]; if (useNormals) n[i] = normals[normalIndex[i]]; if (useUVs) { uvs[i*2] = uvVec[uvIndex[i]*2]; uvs[i*2+1] = uvVec[uvIndex[i]*2 + 1]; } vertexIndex[i] = i; } } BBox Wavefront::ObjectBound() const { BBox bobj; for (int i = 0; i < nverts; i++) bobj = Union(bobj, WorldToObject(p[i])); return bobj; } BBox Wavefront::WorldBound() const { BBox worldBounds; for (int i = 0; i < nverts; i++) worldBounds = Union(worldBounds, p[i]); return worldBounds; } // Refine // generates the triangle mesh shape void Wavefront::Refine(vector > &refined) const { ParamSet paramSet; paramSet.AddInt("indices", vertexIndex, ntris*3); paramSet.AddPoint("P", p, nVerticesTotal); if (n) paramSet.AddNormal("N", n, nVerticesTotal); if (uvs) paramSet.AddFloat("uv", uvs, nVerticesTotal*2); refined.push_back(MakeShape("trianglemesh", ObjectToWorld, reverseOrientation, paramSet)); } // CreateShape // Only one parameter, filename, which directs the program // to the relative path of the object file extern "C" DLLEXPORT Shape *CreateShape(const Transform &o2w, bool reverseOrientation, const ParamSet ¶ms) { string filename = params.FindOneString("filename",""); return new Wavefront(filename.c_str(), o2w, reverseOrientation); }