#ifndef STIM_OBJ_H #define STIM_OBJ_H #include #include #include #include #include #include #include namespace stim{ /** This class provides an interface for loading, saving, and working with Wavefront OBJ files. * The class structure uses similar terminology to the OBJ file structure, so one familiar with it * should be able to work well with this class. * * This class currently provides the ability to load explicitly defined geometric objects, including point lists, * lines, and faces with positions, texture coordinates, and normals. This data is stored in a protected series * of lists that are accessible to classes that extend this one. * * Multiple helper classes are used to facilitate loading, storing, and saving geometry. These are protected * and therefore only designed to be used within the confines of the class. These helper classes include: * * vertex class - contains methods for loading, saving, and storing 1, 2, 3, and 4 component vectors * triplet class - contains methods for loading, saving, and storing indexed vertices used for geometric objects * geometry class - contains a list of triplets used to define a geometric structure, such as a face or line */ enum obj_type { OBJ_NONE, OBJ_LINE, OBJ_FACE, OBJ_POINTS }; template class obj{ protected: enum token_type { OBJ_INVALID, OBJ_V, OBJ_VT, OBJ_VN, OBJ_P, OBJ_L, OBJ_F }; struct vertex : public stim::vec{ using vec::push_back; using vec::size; using vec::at; //basic constructors (call the stim::vector constructors) vertex(){} vertex(T x, T y) : stim::vec(x, y){} vertex(T x, T y, T z) : stim::vec(x, y, z){} vertex(T x, T y, T z, T w) : stim::vec(x, y, z, w){} //constructor creates a vertex from a line string vertex(std::string line){ //create a temporary vector used to store string tokens std::vector tokens = stim::parser::split(line, ' '); //create temporary storage for casting the token values T val; //for each token (skipping the first) for(unsigned int i = 1; i < tokens.size(); i++){ std::stringstream ss(tokens[i]); //create a stringstream object for casting ss>>val; //cast the token to the correct numerical value push_back(val); //push the coordinate into the vertex } } //output the vertex as a string std::string str(){ std::stringstream ss; for(int i = 0; i < size(); i++){ if(i > 0) ss<<' '; ss<{ //create a triplet given a parameter list (OBJ indices start at 1, so 0 can be used to indicate no value) triplet(unsigned int v, unsigned int vt = 0, unsigned int vn = 0){ push_back(v); if(vn != 0){ push_back(vt); push_back(vn); } else if(vt != 0) push_back(vt); } //build a triplet from a string triplet(std::string s){ //create a temporary vector used to store string tokens std::vector tokens = stim::parser::split(s, '\\'); //std::cout<<"processing triplet: "<= 1) push_back( atoi( tokens[0].c_str() ) ); if(tokens.size() >= 2){ if(tokens[1].length()) push_back( atoi( tokens[1].c_str() ) ); else push_back(0); } if(tokens.size() == 3) push_back( atoi( tokens[2].c_str() ) ); } //output the geometry as a string std::string str(){ std::stringstream ss; ss<{ using std::vector::size; using std::vector::at; using std::vector::push_back; geometry(){} //constructs a geometry object from a line read from an OBJ file geometry(std::string line){ //create a temporary vector used to store string tokens std::vector tokens = stim::parser::split(line, ' '); //for each triplet in the line for(unsigned int i = 1; i < tokens.size(); i++){ //construct the triplet and push it to the geometry push_back( triplet(tokens[i]) ); } } std::string str(){ std::stringstream ss; for(unsigned int i = 0; i < size(); i++){ if(i != 0) ss<<' '; ss< V; //vertex spatial position std::vector VN; std::vector VT; //structure lists std::vector L; //list of lines std::vector P; //list of points structures std::vector F; //list of faces //information for the current geometric object geometry current_geo; vertex current_vt; vertex current_vn; //flags for the current geometric object obj_type current_type; bool geo_flag_vt; bool geo_flag_vn; bool vert_flag_vt; bool vert_flag_vn; void update_vt(vertex vt){ current_vt = vt; //the geometry vertex texture flag can only be set for the first vertex if(current_geo.size() == 0) geo_flag_vt = true; vert_flag_vt = true; } void update_vn(vertex vn){ current_vn = vn; //the geometry normal flag can only be set for the first vertex if(current_geo.size() == 0) geo_flag_vn = true; vert_flag_vn = true; } //create a triple and add it to the current geometry void update_v(vertex vv){ unsigned int v; unsigned int vt = 0; unsigned int vn = 0; //if the current geometry is using a texture coordinate, add the current texture coordinate to the geometry if(geo_flag_vt){ if(vert_flag_vt) //don't add it more than once VT.push_back(current_vt); vt = VT.size(); } //if the current geometry is using a normal, add the current texture coordinate to the geometry if(geo_flag_vn){ if(vert_flag_vn) //don't add it more than once VN.push_back(current_vn); vn = VN.size(); } //add the current vertex position to the geometry V.push_back(vv); v = V.size(); //create a triplet and add it to the current geometry current_geo.push_back(triplet(v, vt, vn)); //clear the vertex flags vert_flag_vt = false; vert_flag_vn = false; } void init(){ //clear all lists V.clear(); VT.clear(); VN.clear(); P.clear(); L.clear(); F.clear(); //initialize all of the flags current_type = OBJ_NONE; geo_flag_vt = false; geo_flag_vn = false; vert_flag_vt = false; vert_flag_vn = false; } //gets the type of token representing the entry in the OBJ file token_type get_token(std::string s){ //if the line contains a vertex if(s[0] == 'v'){ if(s[1] == ' ') return OBJ_V; if(s[1] == 't') return OBJ_VT; if(s[1] == 'n') return OBJ_VN; } if(s[0] == 'l' && s[1] == ' ') return OBJ_L; if(s[0] == 'p' && s[1] == ' ') return OBJ_P; if(s[0] == 'f' && s[1] == ' ') return OBJ_F; return OBJ_INVALID; } public: /// Constructor loads a Wavefront OBJ file obj(std::string filename){ load(filename); } //functions for setting the texture coordinate for the next vertex void TexCoord(T x){ update_vt(vertex(x));} void TexCoord(T x, T y){ update_vt(vertex(x, y));} void TexCoord(T x, T y, T z){ update_vt(vertex(x, y, z));} void TexCoord(T x, T y, T z, T w){ update_vt(vertex(x, y, z, w));} //functions for setting the normal for the next vertex void Normal(T x){ update_vn(vertex(x));} void Normal(T x, T y){ update_vn(vertex(x, y));} void Normal(T x, T y, T z){ update_vn(vertex(x, y, z));} void Normal(T x, T y, T z, T w){ update_vn(vertex(x, y, z, w));} //functions for setting the next vertex position (note that this updates the current geometry) void Vertex(T x){ update_v(vertex(x));} void Vertex(T x, T y){ update_v(vertex(x, y));} void Vertex(T x, T y, T z){ update_v(vertex(x, y, z));} void Vertex(T x, T y, T z, T w){ update_v(vertex(x, y, z, w));} ///This function starts drawing of a primitive object, such as a line, face, or point set /// @param t is the type of object to be drawn: OBJ_POINTS, OBJ_LINE, OBJ_FACE void Begin(obj_type t){ current_type = t; } /// This function terminates drawing of a primitive object, such as a line, face, or point set void End(){ //copy the current object to the appropriate list switch(current_type){ case OBJ_NONE: std::cout<<"STIM::OBJ error, objEnd() called before objBegin()."< getV(unsigned int i){ stim::vec v = V[i]; return v; } stim::vec centroid(){ //get the number of coordinates unsigned int N = V[0].size(); //allocate space for the minimum and maximum coordinate points (bounding box corners) stim::vec vmin, vmax; vmin.resize(N); vmax.resize(N); //find the minimum and maximum value for each coordinate unsigned int NV = V.size(); for(unsigned int v = 0; v < NV; v++) for(unsigned int i = 0; i < N; i++){ if(V[v][i] < vmin[i]) vmin[i] = V[v][i]; if(V[v][i] > vmax[i]) vmax[i] = V[v][i]; } //find the centroid using the min and max points stim::vec c = vmin * 0.5 + vmax * 0.5; return c; } /// Returns the number of lines in the OBJ structure unsigned int numL(){ return L.size(); } /// Returns all points in the line corresponding to a given index /// @param i is the index of the desired line std::vector< stim::vec > getL_V(unsigned int i){ //get the number of points in the specified line unsigned int nP = L[i].size(); //create a vector of points std::vector< stim::vec > l; //set the size of the vector l.resize(nP); //copy the points from the point list to the stim vector unsigned int pi; for(unsigned int p = 0; p < nP; p++){ //get the index of the geometry point pi = L[i][p][0] - 1; //get the coordinates of the current point stim::vec newP = V[pi]; //copy the point into the vector l[p] = newP; } return l; } /// Returns the vertex indices for the specified line /// @param i is the index of the line std::vector< unsigned int > getL_Vi(unsigned int i){ unsigned int nP = L[i].size(); std::vector< unsigned int > l; //set the size of the vector l.resize(nP); //copy the indices from the geometry structure to the array for(unsigned int p = 0; p < nP; p++){ l[p] = L[i][p][0]; } return l; } }; }; #endif