Geri/0040700000175200001440000000000010112514544010426 5ustar lmbusersGeri/GeriFuncs.h0100644000175200001440000000174207654303133012506 0ustar lmbusers/******************************************************************************\ * Miscellaneous functions for Geri. * * * * Leandro Motta Barros * \******************************************************************************/ #ifndef _GERI_FUNCS_H_ #define _GERI_FUNCS_H_ #include "CatmullClark.h" #include /** Renders a given \texttt{Mesh}. The parameters define if the object should be * rendered either solid or wireframe (depending on the \texttt{wireframe} * parameter) and if edges should be rendered when the object is solid * (depending in the \texttt{drawEdgesIfSolid} parameter). */ void Render(const Mesh& mesh, bool wireFrame, bool drawEdgesIfSolid); /// Saves the given mesh as an OBJ file. void SaveOBJ(const Mesh& mesh, const std::string& fileName); #endif // _GERI_FUNCS_H_ Geri/Makefile0100644000175200001440000000243710112510135012073 0ustar lmbusers# # General settings # CXX = g++ -pthread COMMON_FLAGS = -I. # # Optimization flags # # Safe flags #CXXFLAGS = $(COMMON_FLAGS) -Wall -O2 -march=i686 -mcpu=i686 -DNDEBUG # Good for my computer CXXFLAGS = $(COMMON_FLAGS) -Wall -O3 -march=pentium4 -mcpu=pentium4 -fomit-frame-pointer -funroll-loops -mmmx -msse -msse2 -DNDEBUG # Good for profiling #CXXFLAGS = $(COMMON_FLAGS) -Wall -O3 -march=i686 -mcpu=i686 -funroll-loops -pg -DNDEBUG # Good for debug #CXXFLAGS = $(COMMON_FLAGS) -Wall -g LINKLIBS = -lGL -lglut -lGLU -lX11 -lXmu -lXi -lboost_thread-gcc-mt -lpthread OBJECTS = WEDS.o GeriMain.o GeriFuncs.o CatmullClark.o OpenGLMB/OBJParser.o # # The program # geri: $(OBJECTS) $(CXX) $(CXXFLAGS) -o geri $(OBJECTS) $(LINKLIBS) GeriMain.o: GeriMain.cpp WEDS.h CatmullClark.h GeriFuncs.h $(CXX) $(CXXFLAGS) -c GeriMain.cpp WEDS.o: WEDS.cpp WEDS.h $(CXX) $(CXXFLAGS) -c WEDS.cpp GeriFuncs.o: GeriFuncs.cpp GeriFuncs.h CatmullClark.h $(CXX) $(CXXFLAGS) -c GeriFuncs.cpp CatmullClark.o: CatmullClark.cpp CatmullClark.h WEDS.h $(CXX) $(CXXFLAGS) -c CatmullClark.cpp # # This should be on OpenGLMB Makefile # OpenGLMB/OBJParser.o: OpenGLMB/OBJParser.cpp OpenGLMB/OBJParser.h $(CXX) $(CXXFLAGS) -c OpenGLMB/OBJParser.cpp -o OpenGLMB/OBJParser.o # # Bureaucracy # clean: rm -f $(OBJECTS) Test Geri/CatmullClark.cpp0100644000175200001440000002037707654303175013545 0ustar lmbusers/******************************************************************************\ * CatmullClark * * An implementation of the Catmull-Clark algorithm to create subdivision * * surfaces. * * Leandro Motta Barros * * April 02003 * \******************************************************************************/ #include "CatmullClark.h" #include /// Calculates the centroid of a given face in a \texttt{Mesh}. Abbott::Point<3> CalcCentroid(const Mesh& mesh, unsigned face) { Abbott::Point<3> ret; ret[0] = ret[1] = ret[2] = 0.0; for (unsigned i = 0; i < mesh.faces[face].size(); ++i) // face 1 is dummy { ret[0] += mesh.vertices[mesh.faces[face][i]][0]; ret[1] += mesh.vertices[mesh.faces[face][i]][1]; ret[2] += mesh.vertices[mesh.faces[face][i]][2]; } ret[0] /= mesh.faces[face].size(); ret[1] /= mesh.faces[face].size(); ret[2] /= mesh.faces[face].size(); return ret; } /** Insert \texttt{vertex} in \texttt{vertices} if it is not already there. This * is used to avoid to insert the same vertex multiple times. The check for * "already there" is approximate, so that rounding errors are (hopefully) * avoided. * @param vertices The \texttt{std::vector} of vertices in which * \texttt{vertex} will be inserted if not already present. * @param vertex The vertex to be inserted in \texttt{vertices}. * @return The index where \texttt{vertex} can be found in \texttt{vertices} * (that is, the new index where it was inserted or the index where it * was previously). */ unsigned InsertVertex(Mesh::VerticesVec& vertices, const Abbott::Point<3>& vertex) { const double DIST_TO_BE_EQUAL = 1e-10; // check if it is already there Mesh::VerticesVec::size_type index = 0; for (Mesh::VerticesVec::iterator p = vertices.begin(); p != vertices.end(); ++p, ++index) { if (Abbott::Distance(vertex, *p) <= DIST_TO_BE_EQUAL) return index; } // not found, insert it vertices.push_back(vertex); return vertices.size()-1; } // - CatmullClarkSubdivide ----------------------------------------------------- Mesh CatmullClarkSubdivide(const Mesh& mesh, WEDS& weds, std::vector& extraEdgeData) { typedef std::vector VecUInt; typedef Abbott::Point<3> Vertex; Mesh newMesh = mesh; // start with a copy of the original mesh newMesh.vertices.reserve(mesh.vertices.size()*4); newMesh.faces.reserve(mesh.faces.size()*4); // in the two following variables (which are vectors of vectors), the first // index identifies the vertex being processed and the second is a sequential // identifier for the vertex. std::vector f; // new face points std::vector e; // new edge points std::vector v; // new vertex points f.reserve(newMesh.vertices.size()); e.reserve(newMesh.vertices.size()); v.reserve(newMesh.vertices.size()); // dummies f.push_back(VecUInt()); e.push_back(VecUInt()); v.push_back(Vertex()); const unsigned currentVertexCount = newMesh.vertices.size(); // calculate face points { // just a scope for 'facePoints' std::map facePoints; // maps face to face point for (unsigned vertexIndex = 1; vertexIndex < currentVertexCount; ++vertexIndex) { f.push_back(VecUInt()); std::pair edgesAndFaces = weds.getEdgesAndFacesSharingVertex(vertexIndex); // edges and faces "using" the vertex 'vertexIndex' const VecUInt& faces = edgesAndFaces.second; for (VecUInt::const_iterator face = faces.begin(); face != faces.end(); ++face) { std::map::const_iterator it = facePoints.find(*face); if (it != facePoints.end()) f[vertexIndex].push_back(it->second); else { newMesh.vertices.push_back(CalcCentroid(newMesh, *face)); f[vertexIndex].push_back(newMesh.vertices.size()-1); facePoints[*face] = newMesh.vertices.size()-1; } } } } // calculate edge points { // just a scope for 'edgePoints' std::map edgePoints; // maps edge to edge point for (unsigned vertexIndex = 1; vertexIndex < currentVertexCount; ++vertexIndex) { e.push_back(VecUInt()); std::pair edgesAndFaces = weds.getEdgesAndFacesSharingVertex(vertexIndex); const VecUInt& edges = edgesAndFaces.first; // calculate the 'e' vertices for (VecUInt::size_type j = 0; j < edges.size(); ++j) { const unsigned edge = edges[j]; std::map::const_iterator it = edgePoints.find(edge); if (it != edgePoints.end()) e[vertexIndex].push_back(it->second); else { // the formula seen on DeRose's paper uses 'j-1'. I'm using 'j+1' // because my edges are ordered differently. const unsigned jm1 = (j+1) % edges.size(); const Vertex& v_i = newMesh.vertices[vertexIndex]; const Vertex& e_i_j = newMesh.vertices[weds.getOtherVertex(vertexIndex, edge)]; const Vertex& f_ip1_jm1 = newMesh.vertices[f[vertexIndex][jm1]]; const Vertex& f_ip1_j = newMesh.vertices[f[vertexIndex][j]]; const Vertex e_ip1_j = (v_i + e_i_j + f_ip1_jm1 + f_ip1_j) / 4.0; newMesh.vertices.push_back(e_ip1_j); e[vertexIndex].push_back(newMesh.vertices.size()-1); edgePoints[edge] = newMesh.vertices.size()-1; } } } } // calculate new vertex points for (unsigned vertexIndex = 1; vertexIndex < currentVertexCount; ++vertexIndex) { std::pair edgesAndFaces = weds.getEdgesAndFacesSharingVertex(vertexIndex); const VecUInt& edges = edgesAndFaces.first; // find the new 'v^{i+1}' vertex const double n = static_cast(edges.size()); // valence as floating point const Vertex& v_i = newMesh.vertices[vertexIndex]; const Vertex term1 = ((n-2)/n) * v_i; Vertex term2; term2[0] = term2[1] = term2[2] = 0.0; Vertex term3; term3[0] = term3[1] = term3[2] = 0.0; for (VecUInt::size_type j = 0; j < edges.size(); ++j) { const unsigned edge = edges[j]; const Vertex& e_i_j = newMesh.vertices[weds.getOtherVertex(vertexIndex, edge)]; term2 += e_i_j; const Vertex& f_ip1_j = newMesh.vertices[f[vertexIndex][j]]; term3 += f_ip1_j; } const double div = n*n; term2 /= div; term3 /= div; const Vertex v_ip1 = term1 + term2 + term3; v.push_back(v_ip1); } // // TODO: this loop is almost equal the previous one. can't this be moved there? // // all new vertices are calculated; add them and the new edges to the mesh newMesh.faces.clear(); newMesh.faces.push_back(VecUInt()); for (unsigned vertexIndex = 1; vertexIndex < currentVertexCount; ++vertexIndex) { // update v_i to v_i+1 newMesh.vertices[vertexIndex] = v[vertexIndex]; // recreate the faces std::pair edgesAndFaces = weds.getEdgesAndFacesSharingVertex(vertexIndex); const VecUInt& edges = edgesAndFaces.first; for (VecUInt::size_type j = 0; j < edges.size(); ++j) { newMesh.faces.push_back(VecUInt()); const unsigned vertex1 = vertexIndex; const unsigned vertex2 = e[vertexIndex][j]; const unsigned vertex3 = f[vertexIndex][j]; const unsigned vertex4 = e[vertexIndex][j == 0 ? edges.size()-1 : j-1]; newMesh.faces.back().push_back(vertex1); newMesh.faces.back().push_back(vertex2); newMesh.faces.back().push_back(vertex3); newMesh.faces.back().push_back(vertex4); } } weds = WEDS(newMesh.vertices, newMesh.faces); return newMesh; } Geri/README0100644000175200001440000000200410112514540011305 0ustar lmbusersGeri -- Subdivision Surfaces By Leandro Motta Barros This is an implementation of the Catmull-Clark algorithm for subdivision surfaces. It was an assignment for the "Special Topics on Computer Graphics" course, taught by Marcelo Walter and Soraia Raupp Musse, at Unisinos University, Brazil, in the first semester of 02003. There is no documentation on how to use it. Please check the function 'KeyboardCallback()' in 'GeriMain.cpp' for some information. The algorithm itself is documented in "Subdivision Surfaces in Character Animation", by Tony DeRose, Michael Kass and Tien Truong (this was published in SIGGRAPH '98). (But this implementation ignores all that "sharp edges" stuff) By the way, the name "Geri" is a reference to the main character of the great animation "Geri's Game", by Pixar. Geri was modeled with Catmull-Clark subdivision surfaces. [This was written in August 02004, shortly after minor modification to the makefile in order to allow it compiling and linking with newer versions of GCC and Boost] Geri/WEDS.h0100644000175200001440000001216707650327343011373 0ustar lmbusers/******************************************************************************\ * WEDS * * An implementation of the Winged Edge Data Structure [Baumgart 01975]. In * * fact, this is not a generic implementation. Is biased towards its original * * intent: the implementation of the Catmull-Clark surface subdivision * * technique. * * Leandro Motta Barros * * March 02003 * \******************************************************************************/ #ifndef _LMB_WEDS_H_ #define _LMB_WEDS_H_ #include #include #include #include #include #include /// Stores data for an edge. struct EdgeData { /// The vertex where this edge starts. unsigned startVertex; /// The vertex where this edge ends. unsigned endVertex; /// The face to the left of this edge. unsigned leftFace; /// The face to the right of this edge. unsigned rightFace; /// The edge that precedes this one when traversing the left face. unsigned leftTraversePred; /// The edge that succeeds this one when traversing the left face. unsigned leftTraverseSucc; /// The edge that precedes this one when traversing the right face. unsigned rightTraversePred; /// The edge that succeeds this one when traversing the right face. unsigned rightTraverseSucc; }; /// Stores information about a 3D model in a Winged Edge Data Structure. class WEDS { public: typedef std::vector EdgesVec; typedef std::vector > VerticesVec; typedef std::vector Face; // each element is an index to // the vertex vector typedef std::vector FacesVec; /** Constructs a \texttt{WEDS}. The constructor automatically computes the * data that a WEDS is supposed to store. */ WEDS (const VerticesVec& vertices, const FacesVec& faces); /** Returns the valence of a given vertex (\texttt{i.e.}, the number of * edges "sharing" the vertex). * @param vertex The index of the desired vertex. * @return The valence of \texttt{vertex}. */ unsigned getValence(unsigned vertex); /** Returns the edges that share a given vertex. The number of returned * edges is equal the vertex's valence (that's quite obvious: this is the * definition of valence). */ std::vector getEdgesSharingVertex(unsigned vertex); /** Returns the faces that share a given vertex. The number of returned * faces seems to be equal the vertex's valence. */ std::vector getFacesSharingVertex(unsigned vertex); /** Returns the faces and the edges that share a given vertex. * Furthermore, there is a nice guarantee regarding the order of the * returned data: the edge $n$ has faces $n$ and $n+1$ as neighbors * (notice that $n+1$ should be taken modulo the valence). * @param The desired vertex. * @return A \texttt{std::pair} where \texttt{first} are the edges * are \texttt{second} are the faces. */ std::pair, std::vector > getEdgesAndFacesSharingVertex(unsigned vertex); /** Given an edge and a vertex, this method returns the other vertex that * define the edge. */ unsigned getOtherVertex(unsigned vertex, unsigned edge); private: /** Reprocesses the WEDS data from the vertices and faces passed to the * constructor. Current implementation is probably much more complex than * necessary (Big-Oh-ly speaking). */ void reprocess(); /// Does an edge from here to there already exists? bool existsEdge(unsigned oneVertex, unsigned otherVertex) const; /** Returns pair containing the two faces sharing a given edge. The first * pair's element is the left face; the second is the right face. */ std::pair getLeftAndRightFaces(unsigned from, unsigned to) const; /** Get the edge that precedes and the edges that succeeds \texttt{edge} * when traversing \texttt{face}. They are returned as a * \texttt{std::pair}, with the predecessor \texttt{first} and the * successor \texttt{second}. */ std::pair getPredSuccEdge(unsigned face, unsigned edge) const; EdgesVec edges_; VerticesVec vertices_; FacesVec faces_; // here go a few "indices" to make the whole thing faster (the old trick // of exchanging memory for speed). I'm using 'multimap' as a poor man's // hash table). typedef std::multimap IndexType; IndexType indexByStart_; IndexType indexByEnd_; IndexType indexByLeftFace_; IndexType indexByRightFace_; IndexType indexFacesByVertex_; }; #endif // _LMB_WEDS_H_ Geri/CatmullClark.h0100644000175200001440000000372507644665567013227 0ustar lmbusers/******************************************************************************\ * CatmullClark * * An implementation of the Catmull-Clark algorithm to create subdivision * * surfaces. * * Leandro Motta Barros * * April 02003 * \******************************************************************************/ #ifndef _CATMULL_CLARK_H_ #define _CATMULL_CLARK_H_ #include #include #include "WEDS.h" /// A renderable mesh of polygons. struct Mesh { typedef std::vector > VerticesVec; typedef std::vector > FacesVec; /// Constructors for convenience. Mesh() { } Mesh(const VerticesVec& v, const FacesVec& f) : vertices(v), faces(f) { } /// Vertices (with a dummy vertex at index 0) VerticesVec vertices; /// Faces (with a dummy face at index 0) FacesVec faces; }; /// Additional per-edge data (beyond WEDS information). struct ExtraEdgeData { /// Indicates whether the edge is sharp or not. bool sharp; /// Flags whether the edge had its $i$ vertex already computed. bool eVertexDone; }; /** Subdivides the mesh passed as parameter using the Catmull-Clark algorithm. * @param mesh The mesh to be subdivided * @param edgeData Information about the edges in \texttt{mesh}, as stored in a * Winged Edge Data Structure. This is modified to keep up-to-date with * new mesh being created. * @param extraEdgeData More information about the edges (beyond WEDS). This is * modified to keep up-to-date with new mesh being created. */ Mesh CatmullClarkSubdivide(const Mesh& mesh, WEDS& weds, std::vector& extraEdgeData); #endif // _CATMULL_CLARK_H_ Geri/GeriFuncs.cpp0100644000175200001440000000702707654303142013043 0ustar lmbusers/******************************************************************************\ * Miscellaneous functions for Geri. * * * * Leandro Motta Barros * \******************************************************************************/ #include "GeriFuncs.h" #include #include #include #include GLfloat EDGE_COLOR[] = { 0.0f, 0.0f, 1.0f, 0.0f }; GLfloat FACE_COLOR[] = { 0.75f, 0.75f, 0.75f, 0.0f }; // - Render -------------------------------------------------------------------- // function object to render a face class RenderFace { public: RenderFace(const std::vector > vertices, bool wireFrame, bool drawEdgesIfSolid) : vertices_(vertices), wireFrame_(wireFrame), drawEdgesIfSolid_(drawEdgesIfSolid) { } void operator()(const std::vector& face) { using namespace Abbott; Vector<3> normal = CrossProd(vertices_[face[1]] - vertices_[face[0]], vertices_[face[2]] - vertices_[face[1]]).normalized(); glNormal3f(normal[0], normal[1], normal[2]); // draw edges if (wireFrame_ || drawEdgesIfSolid_) { glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, EDGE_COLOR); glBegin(GL_LINE_STRIP); for(std::vector::const_iterator p = face.begin(); p != face.end(); ++p) glVertex3f(vertices_[*p][0], vertices_[*p][1], vertices_[*p][2]); glEnd(); } // draw face if (!wireFrame_) { glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, FACE_COLOR); glBegin(GL_POLYGON); for(std::vector::const_iterator p = face.begin(); p != face.end(); ++p) glVertex3f(vertices_[*p][0], vertices_[*p][1], vertices_[*p][2]); glEnd(); } } private: const std::vector >& vertices_; const bool wireFrame_; const bool drawEdgesIfSolid_; }; void Render(const Mesh& mesh, bool wireFrame, bool drawEdgesIfSolid) { // just render every face (except face 0, which is dummy) std::for_each(mesh.faces.begin()+1, mesh.faces.end(), RenderFace(mesh.vertices, wireFrame, drawEdgesIfSolid)); } // - SaveOBJ ------------------------------------------------------------------- void SaveOBJ(const Mesh& mesh, const std::string& fileName) { std::ofstream ofs(fileName.c_str()); if (!ofs.is_open()) throw std::runtime_error("Could not open file '" + fileName + "' for writing"); // FIXME: I need a name ofs << "# OBJ file created by Geri (using subdivision surfaces if you are wondering...)\n" << "# By Leandro Motta Barros\n\n"; for (Mesh::VerticesVec::const_iterator p = mesh.vertices.begin()+1; // first is dummy p != mesh.vertices.end(); ++p) { ofs << "v " << (*p)[0] << ' ' << (*p)[1] << ' ' << (*p)[2] << '\n'; } ofs << '\n'; for (Mesh::FacesVec::const_iterator p = mesh.faces.begin()+1; // first is dummy p != mesh.faces.end(); ++p) { ofs << "f "; for (std::vector::const_iterator q = p->begin(); q != p->end(); ++q) ofs << *q << ' '; ofs << '\n'; } } Geri/OpenGLMB/0040755000175200001440000000000010112511046011775 5ustar lmbusersGeri/OpenGLMB/Exception.h0100644000175200001440000000510007643642547014127 0ustar lmbusers/******************************************************************************\ * OpenGLMB, a library of OpenGL-related stuff I end up using over and over. * * * * By Leandro Motta Barros * * * ******************************************************************************** * * * Exception * * A minimal error reporting facility for OpenGL (I know it's not the ideal, * * but it's better than ignoring error return codes at all). * * * * Uses 'lexical_cast' from the Boost libraries (www.boost.org) * * * \******************************************************************************/ #ifndef _OPENGLMB_EXCEPTION_ #define _OPENGLMB_EXCEPTION_ #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include #endif #include #include #include namespace OpenGLMB { /// An exception for OpenGL errors class OpenGLException: public std::exception { public: OpenGLException(const char* file, int line) : file_(file), line_(line) { } ~OpenGLException() throw() { } const char* what() const throw() { try { return ("OpenGL call failed at '" + file_ + ':' + boost::lexical_cast(line_) + '\'').c_str(); } catch(...) { // I just hope nobody see this message ever... return "Ooops... an error occurred while attempting to report an OpenGL error"; } } private: std::string file_; int line_; }; /// Throws an \texttt{OpenGLException} if an OpenGL error code was returned. # ifdef _WIN32 # define ThrowOnOpenGLError() ; // FIXME: Why is the Win32 version defined to nothing? # else # define ThrowOnOpenGLError() \ if (glGetError() != GL_NO_ERROR) \ throw OpenGLMB::OpenGLException(__FILE__, __LINE__); # endif // _WIN32 } // namespace OpenGLMB #endif // _OPENGLMB_EXCEPTION_ Geri/OpenGLMB/OBJParser.cpp0100644000175200001440000002237507663017712014320 0ustar lmbusers/******************************************************************************\ * OpenGLMB, a library of OpenGL-related stuff I end up using over and over. * * * * By Leandro Motta Barros * * * ******************************************************************************** * * * OBJParser * * A parser for 3D models in the "OBJ" format. Technically not OpenGL stuff, * * but closely related. * * * * Uses the 'spirit' library from Boost (www.boost.org) and my own 'Abbott' * * library. * * * \******************************************************************************/ // FIXME: This code is not thread safe. #ifdef _MSC_VER #pragma warning(disable: 4786) // name truncated to 255 chars in debug info #endif #if _WIN32 #include #endif #include "OBJParser.h" #include #include #include #include #include #include #include #include #include #include using boost::spirit::ch_p; using boost::spirit::anychar_p; using boost::spirit::eol_p; using boost::spirit::epsilon_p; using boost::spirit::str_p; using boost::spirit::real_p; using boost::spirit::end_p; using boost::spirit::repeat_p; using boost::spirit::uint_p; using boost::spirit::space_p; using boost::spirit::comment_p; using boost::spirit::lexeme_d; namespace OpenGLMB { // - Semantic Actions Stuff ------------------------------------------------- namespace { typedef boost::spirit::file_iterator<> IteratorT; typedef Abbott::Point<3> Vertex; typedef Abbott::Vector<3> Normal; std::vector Normals; std::vector Vertices; // - CalcNormal ---------------------------------------------------------- void CalcNormal (const Vertex& v1, const Vertex& v2, const Vertex& v3, Normal& n) { Vertex vc1, vc2; // Find the first vector vc1[0] = v2[0] - v1[0]; vc1[1] = v2[1] - v1[1]; vc1[2] = v2[2] - v1[2]; // Find the second vector vc2[0] = v3[0] - v1[0]; vc2[1] = v3[1] - v1[1]; vc2[2] = v3[2] - v1[2]; // Cross product between vc1 and vc2 n[0] = (vc1[1] * vc2[2]) - (vc1[2] * vc2[1]); n[1] = (vc1[2] * vc2[0]) - (vc1[0] * vc2[2]); n[2] = (vc1[0] * vc2[1]) - (vc1[1] * vc2[0]); // Normalization const double len = std::sqrt(n[0] * n[0] + n[1] * n[1] + n[2] * n[2]); n[0] /= len; n[1] /= len; n[2] /= len; } // - Face ---------------------------------------------------------------- class Face { public: void addVertex(unsigned v) { vertices_.push_back(v); } void addNormal(unsigned n) { normals_.push_back(n); } void clear() { vertices_.clear(); normals_.clear(); } void render() const { assert(vertices_.size() == normals_.size()); Normal normal; if (normals_[0] == 0) { CalcNormal(Vertices[vertices_[0]], Vertices[vertices_[1]], Vertices[vertices_[2]], normal); } glBegin(GL_POLYGON); for (unsigned i = 0; i < vertices_.size(); ++i) { if (normals_[i] != 0) glNormal3f(Normals[normals_[i]][0], Normals[normals_[i]][1], Normals[normals_[i]][2]); else glNormal3f(normal[0], normal[1], normal[2]); glVertex3f(Vertices[vertices_[i]][0], Vertices[vertices_[i]][1], Vertices[vertices_[i]][2]); } glEnd(); } std::vector getVertices() const { return vertices_; } private: std::vector vertices_; std::vector normals_; }; std::vector Faces; typedef std::vector::const_iterator FacesConstIter; Vertex AuxVertex; Normal AuxNormal; Face AuxFace; // - Semantic Actions ---------------------------------------------------- void AddVertex(IteratorT, IteratorT) { Vertices.push_back(AuxVertex); } void AddNormal(IteratorT, IteratorT) { Normals.push_back(AuxNormal); } void AddFace(IteratorT, IteratorT) { Faces.push_back(AuxFace); } void BeginFace(const char) { AuxFace.clear(); } void AddVertexToFace(unsigned vertex) { AuxFace.addVertex(vertex); } void AddNormalToFace(unsigned normal) { AuxFace.addNormal(normal); } void AddDummyNormalToFace(IteratorT, IteratorT) { AuxFace.addNormal(0); } } // - The Parser Definition -------------------------------------------------- template OBJParser::definition::definition(const OBJParser& self) { // - ---------------------------------------------------------- obj_file = *(vertex | normal | face | group) >> end_p ; // - ------------------------------------------------------------ vertex = ('v' >> real_p[boost::spirit::assign(AuxVertex[0])] >> real_p[boost::spirit::assign(AuxVertex[1])] >> real_p[boost::spirit::assign(AuxVertex[2])])[&AddVertex] ; // - ------------------------------------------------------------ normal = ("vn" >> real_p[boost::spirit::assign(AuxNormal[0])] >> real_p[boost::spirit::assign(AuxNormal[1])] >> real_p[boost::spirit::assign(AuxNormal[2])])[&AddNormal] ; // - -------------------------------------------------------------- face = (ch_p('f')[&BeginFace] >> repeat_p(3, boost::spirit::more) [ uint_p[&AddVertexToFace] >> (('/' >> !uint_p >> '/' >> uint_p[&AddNormalToFace]) | epsilon_p[&AddDummyNormalToFace]) ] )[&AddFace] ; // - ------------------------------------------------------------- group = 'g' >> lexeme_d[*(anychar_p - '\n') >> eol_p]; ; } // - OBJParser::getNormals -------------------------------------------------- std::vector > OBJParser::getNormals() const { return Normals; } // - OBJParser::getVertices ------------------------------------------------- std::vector > OBJParser::getVertices() const { return Vertices; } // - OBJParser::getFaces ---------------------------------------------------- std::vector > OBJParser::getFaces() const { std::vector > faces; faces.push_back(std::vector()); // add a dummy to keep consistency // (valid index begin at 1) for (FacesConstIter face = Faces.begin(); face != Faces.end(); ++face) faces.push_back(face->getVertices()); return faces; } // - OBJParser::parseFile --------------------------------------------------- void OBJParser::parseFile() { // add dummies at index 0 Vertices.push_back(Vertex()); Normals.push_back(Normal()); boost::spirit::file_iterator<> first(fileName_.c_str()); if (!first) { std::string errMsg = "Error opening OBJ file (" + fileName_ + ')'; throw std::runtime_error(errMsg); } boost::spirit::file_iterator<> last = first.make_end(); boost::spirit::parse_info > info = boost::spirit::parse(first, last, *this, comment_p('#') | space_p); if (!info.full) { std::string errMsg = "Error parsing OBJ file (" + fileName_ + ')'; throw std::runtime_error(errMsg.c_str()); } } // - OBJParser::createDisplayList ------------------------------------------- GLuint OBJParser::createDisplayList() { GLuint displayListName = glGenLists(1); ThrowOnOpenGLError(); glNewList(displayListName, GL_COMPILE); for (std::vector::const_iterator pFace = Faces.begin(); pFace != Faces.end(); ++pFace) pFace->render(); glEndList(); ThrowOnOpenGLError(); return displayListName; } } // namespace OpenGLMB Geri/OpenGLMB/OBJParser.h0100644000175200001440000000465607643661211013764 0ustar lmbusers/******************************************************************************\ * OpenGLMB, a library of OpenGL-related stuff I end up using over and over. * * * * By Leandro Motta Barros * * * ******************************************************************************** * * * OBJParser * * A parser for 3D models in the "OBJ" format. Technically not OpenGL stuff, * * but closely related. * * * * Uses the 'spirit' library from Boost (www.boost.org) and my own 'Abbott' * * library. * * * \******************************************************************************/ #ifndef _OPENGLMB_OBJ_PARSER_H_ #define _OPENGLMB_OBJ_PARSER_H_ #ifdef _WIN32 #define WIN32_LEAN_AND_MEAN #include #endif #include #include #include #include #include namespace OpenGLMB { /// A parser for 3D models in the OBJ format class OBJParser: public boost::spirit::grammar { public: OBJParser(const char* fileName) : fileName_(fileName) { } void parseFile(); GLuint createDisplayList(); std::vector > getNormals() const; std::vector > getVertices() const; std::vector > getFaces() const; private: std::string fileName_; // - Grammar Stuff ------------------------------------------------------- public: template struct definition { definition(const OBJParser& self); boost::spirit::rule obj_file, normal, vertex, face, group; const boost::spirit::rule& start() const { return obj_file; } }; }; } // namespace OpenGLMB #endif // _OPENGLMB_OBJ_PARSER_H_ Geri/WEDS.cpp0100644000175200001440000002630507650342071011720 0ustar lmbusers/******************************************************************************\ * WEDS * * An implementation of the Winged Edge Data Structure [Baumgart 01975] * * Leandro Motta Barros * * March 02003 * \******************************************************************************/ #include "WEDS.h" #include #include #include #include #include #include // - operator<< ---------------------------------------------------------------- std::ostream& operator<< (std::ostream& lhs, const EdgeData& rhs) { lhs << rhs.startVertex << '\t' << rhs.endVertex << '\t' << rhs.leftFace << '\t' << rhs.rightFace << '\t' << rhs.leftTraversePred << '\t' << rhs.leftTraverseSucc << '\t' << rhs.rightTraversePred << '\t' << rhs.rightTraverseSucc; return lhs; } // - WEDS::WEDS ---------------------------------------------------------------- WEDS::WEDS (const VerticesVec& vertices, const FacesVec& faces) : vertices_(vertices), faces_(faces) { reprocess(); } // - WEDS::reprocess ----------------------------------------------------------- void WEDS::reprocess() { using Abbott::Point; using namespace std; // reserve memory for the edges (from the Euler formula, $E=V+F-2$). One // extra vertex is used as dummy, hence the '-1' instead of '-2'. edges_.reserve(vertices_.size() + faces_.size() - 1); edges_.push_back(EdgeData()); // add dummy at index 0 // find all edges (i.e., their start and end vertices for (unsigned i = 1; i < faces_.size(); ++i) // index 0 contains a dummy { for (unsigned j = 0; j < faces_[i].size(); ++j) { unsigned from = faces_[i][j]; // 'from' and 'to' are the vertices unsigned to = faces_[i][(j+1) % faces_[i].size()]; if (!existsEdge(from, to)) // ignore edges already processed { EdgeData edge; edge.startVertex = from; edge.endVertex = to; indexByStart_.insert(std::make_pair(edge.startVertex, edges_.size())); indexByEnd_.insert(std::make_pair(edge.endVertex, edges_.size())); edges_.push_back(edge); } } } // fill in the structure of faces indexed by vertex for (unsigned i = 1; i < faces_.size(); ++i) // index 0 contains a dummy { for (unsigned j = 0; j < faces_[i].size(); ++j) { unsigned theVertex = faces_[i][j]; indexFacesByVertex_.insert(std::make_pair(theVertex, i)); } } // find left and right faces for every edge for (unsigned i = 1; i < edges_.size(); ++i) // index 0 contains a dummy { EdgeData& edge = edges_[i]; std::pair edgeFaces = getLeftAndRightFaces(edge.startVertex, edge.endVertex); edge.leftFace = edgeFaces.first; edge.rightFace = edgeFaces.second; indexByLeftFace_.insert(std::make_pair(edge.leftFace, i)); indexByRightFace_.insert(std::make_pair(edge.rightFace, i)); } // now find successors and predecessors of every edge for (unsigned i = 1; i < edges_.size(); ++i) { EdgeData& edge = edges_[i]; std::pair succPredEdge = getPredSuccEdge(edge.leftFace, i); edge.leftTraversePred = succPredEdge.second; edge.leftTraverseSucc = succPredEdge.first; succPredEdge = getPredSuccEdge(edge.rightFace, i); edge.rightTraversePred = succPredEdge.first; edge.rightTraverseSucc = succPredEdge.second; } // Once upon a time this was nice for debugging // cout << "startV\tendV\tlFace\trFace\tltPred\tltSucc\trtPred\trtSucc\n"; // for (EdgesVec::iterator p = edges_.begin()+1; p != edges_.end(); ++p) // cout << *p << '\n'; } // - WEDS::existsEdge ---------------------------------------------------------- bool WEDS::existsEdge(unsigned oneVertex, unsigned otherVertex) const { // look in start std::pair range = indexByStart_.equal_range(oneVertex); if (range.first != indexByStart_.end()) { for (IndexType::const_iterator p = range.first; p != range.second; ++p) if (edges_[p->second].endVertex == otherVertex) return true; } // look in end range = indexByEnd_.equal_range(oneVertex); if (range.first != indexByStart_.end()) { for (IndexType::const_iterator p = range.first; p != range.second; ++p) if (edges_[p->second].startVertex == otherVertex) return true; } return false; } // - WEDS::getLeftAndRightFaces ------------------------------------------------ std::pair WEDS::getLeftAndRightFaces(unsigned from, unsigned to) const { using std::make_pair; std::pair ret = make_pair(0u, 0u); std::pair range = indexFacesByVertex_.equal_range(from); for (IndexType::const_iterator p = range.first; p != range.second; ++p) { const unsigned faceIndex = p->second; const Face& face = faces_[faceIndex]; for (Face::const_iterator vertex = face.begin(); vertex != face.end(); ++vertex) { const unsigned v1 = *vertex; const unsigned v2 = (vertex + 1 == face.end()) ? *face.begin() : *(vertex + 1); if (v1 == from && v2 == to) { ret.first = faceIndex; if (ret.second != 0) return ret; } if (v2 == from && v1 == to) { ret.second = faceIndex; if (ret.first != 0) return ret; } } } assert((ret.first != 0 || ret.second != 0) && "Didn't found left and/or right faces."); return ret; // this is just to make compilers happy (we'll never get here) } // - WEDS::getPredSuccEdge ----------------------------------------------------- std::pair WEDS::getPredSuccEdge(unsigned face, unsigned edge) const { std::pair ret = std::make_pair(0u, 0u); const unsigned from = edges_[edge].endVertex; const unsigned to = edges_[edge].startVertex; std::pair rangeLeft = indexByLeftFace_.equal_range(face); for (IndexType::const_iterator p = rangeLeft.first; p != rangeLeft.second; ++p) { const unsigned& edgeIndex = p->second; const EdgeData& ed = edges_[edgeIndex]; if (edgeIndex != edge) { if (ed.startVertex == to || ed.endVertex == to) { ret.first = edgeIndex; if (ret.second != 0) return ret; } else if (ed.startVertex == from || ed.endVertex == from) { ret.second = edgeIndex; if (ret.first != 0) return ret; } } } std::pair rangeRight = indexByRightFace_.equal_range(face); for (IndexType::const_iterator p = rangeRight.first; p != rangeRight.second; ++p) { const unsigned& edgeIndex = p->second; const EdgeData& ed = edges_[edgeIndex]; if (edgeIndex != edge) { if (ed.startVertex == to || ed.endVertex == to) { ret.first = edgeIndex; if (ret.second != 0) return ret; } else if (ed.startVertex == from || ed.endVertex == from) { ret.second = edgeIndex; if (ret.first != 0) return ret; } } } return ret; } // - WEDS::getEdgesSharingVertex ----------------------------------------------- std::vector WEDS::getEdgesSharingVertex(unsigned vertex) { std::vector ret; for (EdgesVec::size_type edge = 1; edge < edges_.size(); ++edge) if (edges_[edge].startVertex == vertex || edges_[edge].endVertex == vertex) ret.push_back(edge); return ret; } // - WEDS::getValence ---------------------------------------------------------- unsigned WEDS::getValence(unsigned vertex) { unsigned valence = 0; std::pair range = indexByStart_.equal_range(vertex); for (IndexType::const_iterator p = range.first; p != range.second; ++p) ++valence; range = indexByEnd_.equal_range(vertex); for (IndexType::const_iterator p = range.first; p != range.second; ++p) ++valence; return valence; } // - WEDS::getFacesSharingVertex ----------------------------------------------- std::vector WEDS::getFacesSharingVertex(unsigned vertex) { std::vector ret; std::vector edges = getEdgesSharingVertex(vertex); for (std::vector::iterator edge = edges.begin(); edge != edges.end(); ++edge) { if (std::find (ret.begin(), ret.end(), edges_[*edge].leftFace) == ret.end()) ret.push_back(edges_[*edge].leftFace); if (std::find (ret.begin(), ret.end(), edges_[*edge].rightFace) == ret.end()) ret.push_back(edges_[*edge].rightFace); } return ret; } // - WEDS::getEdgesAndFacesSharingVertex --------------------------------------- std::pair, std::vector > WEDS::getEdgesAndFacesSharingVertex(unsigned vertex) { std::vector retEdges; std::vector retFaces; // find the first edge to be processed unsigned nextEdge = edges_.size(); std::pair range = indexByStart_.equal_range(vertex); for (IndexType::const_iterator p = range.first; p != range.second; ++p) if (p->second < nextEdge) nextEdge = p->second; range = indexByEnd_.equal_range(vertex); for (IndexType::const_iterator p = range.first; p != range.second; ++p) if (p->second < nextEdge) nextEdge = p->second; // process the edges unsigned edgesProcessed = 0; const unsigned valence = getValence(vertex); while (edgesProcessed < valence) { assert((edges_[nextEdge].startVertex == vertex || edges_[nextEdge].endVertex == vertex) && "Wrong 'nextEdge'. The algorithm is buggy!"); retEdges.push_back(nextEdge); if (edges_[nextEdge].startVertex == vertex) { retFaces.push_back(edges_[nextEdge].leftFace); nextEdge = edges_[nextEdge].rightTraversePred; } else { retFaces.push_back(edges_[nextEdge].rightFace); nextEdge = edges_[nextEdge].leftTraversePred; } ++edgesProcessed; } return std::make_pair(retEdges, retFaces); } // - WEDS::getOtherVertex ------------------------------------------------------ unsigned WEDS::getOtherVertex(unsigned vertex, unsigned edge) { assert((edges_[edge].startVertex == vertex || edges_[edge].endVertex == vertex) && "The vertex passed as parameter is not part of the edge passed as parameter."); if (edges_[edge].startVertex == vertex) return edges_[edge].endVertex; else return edges_[edge].startVertex; } Geri/GeriMain.cpp0100644000175200001440000003215407654306573012663 0ustar lmbusers/******************************************************************************\ * Geri - Implements subdivision surfaces * * * * Leandro Motta Barros * \******************************************************************************/ #include #include #include #include #include #include #include #include #include #include "CatmullClark.h" #include "GeriFuncs.h" using namespace std; // Constants const int STARTING_WINDOW_SIZE = 350; const GLfloat NEAR_CLIP_PLANE = 0.1; const GLfloat FAR_CLIP_PLANE = 2000.0; const GLfloat FOV_ANGLE = 60.0; const float ROTATION_AMOUNT = 0.07f; const float TRANSLATION_AMOUNT = 0.003f; const GLfloat TEXT_COLOR[] = { 1.0, 1.0, 0.0 }; const int TIMER_MOVE_OBJ = 1; const int MOUSE_MOVE_INTERVAL = 100; // Globals used in GLUT callbacks namespace { // object position an rotation GLfloat ObjPosX = 0.0f; GLfloat ObjPosY = 0.0f; GLfloat ObjPosZ = -10.0f; GLfloat ObjRotX = 0.0f; GLfloat ObjRotY = 0.0f; GLfloat ObjRotZ = 0.0f; // mesh-related variables boost::shared_ptr TheWEDS; std::vector TheExtraEdgeData; std::vector TheMeshes; std::vector::size_type MaxSubdivisionLevelSoFar = 0; std::vector::size_type CurrentSubdivisionLevel = 0; // user-interface bool SingleViewPortMode = false; // use a single viewport? bool WireframeMode = true; bool DrawEdgesWhenSolid = true; enum { AXIS_X, AXIS_Y, AXIS_Z } CurrentAxis = AXIS_X; enum { MODE_ROTATION, MODE_TRANSLATION } CurrentMode = MODE_TRANSLATION; int MouseX; int MouseY; bool MouseDown = false; double MouseDist = 0.0; // other std::string OpenedFileName; bool SubdividingInBackground = false; boost::mutex CurrentSubdivisionLevelMutex; } // - InitOpenGL ---------------------------------------------------------------- void InitOpenGL() { glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glColor3fv(TEXT_COLOR); glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); glMatrixMode(GL_PROJECTION); gluPerspective(FOV_ANGLE, 0.5, NEAR_CLIP_PLANE, FAR_CLIP_PLANE); glMatrixMode(GL_MODELVIEW); ThrowOnOpenGLError(); GLfloat lightDiffuse[] = { 0.75f, 0.75f, 0.75f, 0.0f }; GLfloat lightSpecular[] = { 0.0f, 0.0f, 0.0f, 0.0f }; GLfloat lightAmbient[] = { 0.35f, 0.35f, 0.35f }; glLightfv(GL_LIGHT0, GL_DIFFUSE, lightDiffuse); glLightfv(GL_LIGHT0, GL_SPECULAR, lightSpecular); glLightfv(GL_LIGHT0, GL_AMBIENT, lightAmbient); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glShadeModel(GL_SMOOTH); ThrowOnOpenGLError(); GLfloat LightPosition[] = { 1.0f, 1.0f, 1.0f, 0.0f }; glLightfv(GL_LIGHT0, GL_POSITION, LightPosition); GLfloat ambColor[] = { 0.5, 0.5, 0.5 }; glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, ambColor); ThrowOnOpenGLError(); } // - ReallySubdivide ----------------------------------------------------------- void ReallySubdivide() { // I think we don't need a mutex here, because this is the only place where // 'SubdividingInBackground' is modified. try { SubdividingInBackground = true; TheMeshes.push_back(CatmullClarkSubdivide(TheMeshes.back(), *TheWEDS.get(), TheExtraEdgeData)); ++MaxSubdivisionLevelSoFar; { boost::mutex::scoped_lock lock(CurrentSubdivisionLevelMutex); if (CurrentSubdivisionLevel == MaxSubdivisionLevelSoFar-1) ++CurrentSubdivisionLevel; } SubdividingInBackground = false; glutPostRedisplay(); } catch (std::exception& e) { std::cerr << "Exception caught while subdividing mesh: " << e.what() << ".\n"; SubdividingInBackground = false; } } // - NextSubdivision ----------------------------------------------------------- void NextSubdivision() { if(CurrentSubdivisionLevel == MaxSubdivisionLevelSoFar) { if (!SubdividingInBackground) boost::thread t(ReallySubdivide); } else { { boost::mutex::scoped_lock lock(CurrentSubdivisionLevelMutex); ++CurrentSubdivisionLevel; } glutPostRedisplay(); } } // - PreviousSubdivision-------------------------------------------------------- void PreviousSubdivision() { if (CurrentSubdivisionLevel == 0) return; { boost::mutex::scoped_lock lock(CurrentSubdivisionLevelMutex); --CurrentSubdivisionLevel; } glutPostRedisplay(); } // - SaveObject ---------------------------------------------------------------- void SaveObject() { std::string fileName = OpenedFileName; std::string::size_type lastDot = fileName.find_last_of("."); fileName = fileName.substr(0,lastDot); { boost::mutex::scoped_lock lock(CurrentSubdivisionLevelMutex); fileName += '.' + boost::lexical_cast(CurrentSubdivisionLevel) + ".obj"; std::cout << "Saving subdivided mesh as '" << fileName << "'... "; SaveOBJ(TheMeshes[CurrentSubdivisionLevel], fileName); } std::cout << "done!\n"; } // - DisplayText --------------------------------------------------------------- void OutText(int x, int y, const std::string& text) // %^#$@ language missing nested functions! { glRasterPos2f(x, y); for (std::string::size_type i = 0; i < text.length(); ++i) glutBitmapCharacter(GLUT_BITMAP_9_BY_15, text[i]); } void DisplayText() { glDisable(GL_LIGHTING); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); gluOrtho2D(0.0, glutGet(GLUT_WINDOW_WIDTH), 0.0, glutGet(GLUT_WINDOW_HEIGHT)); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); // mode (rotation and translation) and axis std::string modeAndAxis; if (CurrentMode == MODE_TRANSLATION) modeAndAxis = "Translation"; else modeAndAxis = "Rotation"; modeAndAxis += " - "; switch (CurrentAxis) { case AXIS_X: modeAndAxis += "X Axis"; break; case AXIS_Y: modeAndAxis += "Y Axis"; break; case AXIS_Z: modeAndAxis += "Z Axis"; break; } OutText(3, 20, modeAndAxis); // current subdivision level std::string currentLevel = "Subdivision level: " + boost::lexical_cast(CurrentSubdivisionLevel) + '/' + boost::lexical_cast(MaxSubdivisionLevelSoFar); if (SubdividingInBackground) currentLevel += " (subdividing in background...)"; OutText(3, 2, currentLevel); glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glEnable(GL_LIGHTING); } // - DisplayCallback ----------------------------------------------------------- void PositionObject() // %^#$@ language without nested functions! { glLoadIdentity(); glTranslatef(ObjPosX, ObjPosY, ObjPosZ); glRotatef(ObjRotX, 1.0f, 0.0f, 0.0f); glRotatef(ObjRotY, 0.0f, 1.0f, 0.0f); glRotatef(ObjRotZ, 0.0f, 0.0f, 1.0f); ThrowOnOpenGLError(); } void DisplayCallback() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); ThrowOnOpenGLError(); const int winWidth = glutGet(GLUT_WINDOW_WIDTH); const int winHeight = glutGet(GLUT_WINDOW_HEIGHT); if (SingleViewPortMode) { glViewport(0, 0, winWidth, winHeight); DisplayText(); PositionObject(); Render(TheMeshes[CurrentSubdivisionLevel], WireframeMode, DrawEdgesWhenSolid); } else { // left viewport glViewport(0, 0, winWidth/2, winHeight); DisplayText(); PositionObject(); Render(TheMeshes[0], WireframeMode, DrawEdgesWhenSolid); // right viewport glViewport(winWidth/2+1, 0, winWidth/2, winHeight); PositionObject(); boost::mutex::scoped_lock lock(CurrentSubdivisionLevelMutex); Render(TheMeshes[CurrentSubdivisionLevel], WireframeMode, DrawEdgesWhenSolid); } glutSwapBuffers(); ThrowOnOpenGLError(); } // - ReshapeCallback ----------------------------------------------------------- void ReshapeCallback(int width, int height) { glMatrixMode(GL_PROJECTION); glLoadIdentity(); if (SingleViewPortMode) gluPerspective(FOV_ANGLE, static_cast(width)/height, NEAR_CLIP_PLANE, FAR_CLIP_PLANE); else gluPerspective(FOV_ANGLE, static_cast(width/2)/height, NEAR_CLIP_PLANE, FAR_CLIP_PLANE); glMatrixMode(GL_MODELVIEW); } // - TimerCallback ------------------------------------------------------------- void TimerCallback(int value) { if (value == TIMER_MOVE_OBJ) { if (CurrentMode == MODE_TRANSLATION) { switch (CurrentAxis) { case AXIS_X: ObjPosX += MouseDist * TRANSLATION_AMOUNT; break; case AXIS_Y: ObjPosY += MouseDist * TRANSLATION_AMOUNT; break; case AXIS_Z: ObjPosZ += MouseDist * TRANSLATION_AMOUNT; break; } } else { switch (CurrentAxis) { case AXIS_X: ObjRotX += MouseDist * ROTATION_AMOUNT; break; case AXIS_Y: ObjRotY += MouseDist * ROTATION_AMOUNT; break; case AXIS_Z: ObjRotZ += MouseDist * ROTATION_AMOUNT; break; } // keep angles between 0 and 360 if (ObjRotX > 360.0) ObjRotX -= 360.0; if (ObjRotX < 0.0) ObjRotX += 360.0; if (ObjRotY > 360.0) ObjRotY -= 360.0; if (ObjRotY < 0.0) ObjRotY += 360.0; if (ObjRotZ > 360.0) ObjRotZ -= 360.0; if (ObjRotZ < 0.0) ObjRotZ += 360.0; } if (MouseDown) glutTimerFunc(MOUSE_MOVE_INTERVAL, TimerCallback, TIMER_MOVE_OBJ); glutPostRedisplay(); } } // - MouseCallback ------------------------------------------------------------- void MouseCallback(int button, int state, int x, int y) { if (state == GLUT_DOWN) { MouseDown = true; MouseX = x; MouseY = y; glutTimerFunc(MOUSE_MOVE_INTERVAL, TimerCallback, TIMER_MOVE_OBJ); } else MouseDown = false; } // - MotionCallback ------------------------------------------------------------ void MotionCallback(int x, int y) { using namespace std; MouseDist = sqrt(static_cast((x-MouseX)*(x-MouseX) + (y-MouseY)*(y-MouseY))); if (x < MouseX) MouseDist = -MouseDist; } // - KeyboardCallback ---------------------------------------------------------- void KeyboardCallback (unsigned char key, int, int) { switch(key) { case 't': case 'T': CurrentMode = MODE_TRANSLATION; break; case 'r': case 'R': CurrentMode = MODE_ROTATION; break; case 'x': case 'X': CurrentAxis = AXIS_X; break; case 'y': case 'Y': CurrentAxis = AXIS_Y; break; case 'z': case 'Z': CurrentAxis = AXIS_Z; break; case 'v': case 'V': SingleViewPortMode = !SingleViewPortMode; ReshapeCallback(glutGet(GLUT_WINDOW_WIDTH), glutGet(GLUT_WINDOW_HEIGHT)); break; case 'f': case 'F': WireframeMode = !WireframeMode; break; case 'e': case 'E': DrawEdgesWhenSolid = !DrawEdgesWhenSolid; break; case 's': case 'S': SaveObject(); break; case 'n': case 'N': NextSubdivision(); break; case 'p': case 'P': PreviousSubdivision(); break; case 'q': case 'Q': exit(0); break; } glutPostRedisplay(); } // - main ---------------------------------------------------------------------- int main(int argc, char* argv[]) { try { cout << "Geri -- Subdivision surfaces\n" << "By Leandro Motta Barros\n\n"; if (argc != 2) { cerr << "Usage: " << argv[0] << " \n"; exit(1); } OpenGLMB::OBJParser parser(argv[1]); OpenedFileName = argv[1]; parser.parseFile(); TheMeshes.push_back(Mesh(parser.getVertices(), parser.getFaces())); TheWEDS = boost::shared_ptr (new WEDS(TheMeshes[0].vertices, TheMeshes[0].faces)); ThrowOnOpenGLError(); // I would be surprised if an OpenGL error occurs // at this point // Init GLUT glutInitWindowSize(STARTING_WINDOW_SIZE*2, STARTING_WINDOW_SIZE); glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH); ThrowOnOpenGLError(); glutCreateWindow("Geri"); InitOpenGL(); glutDisplayFunc(DisplayCallback); glutKeyboardFunc(KeyboardCallback); glutReshapeFunc(ReshapeCallback); glutMouseFunc(MouseCallback); glutMotionFunc(MotionCallback); // Enter GLUT main loop glutMainLoop(); } catch (OpenGLMB::OpenGLException& e) { std::cerr << "OpenGL exception caught in main: " << e.what() << ".\n"; exit(1); } catch (std::exception& e) { std::cerr << "Exception caught in main: " << e.what() << ".\n"; exit(1); } catch (...) { std::cerr << "Unknown exception caught in main.\n"; exit(1); } } Geri/Abbott/0040755000175200001440000000000010112507455011656 5ustar lmbusersGeri/Abbott/Vector.h0100644000175200001440000001616307646147525013315 0ustar lmbusers/******************************************************************************\ * Abbott, a library of usefull math stuff * * The library is named after Edwin A. Abbott (01828--01926), author of the * * great book "Flatland: A Romance of Many Dimensions". * * * * By Leandro Motta Barros * * * ******************************************************************************** * * * Vector * * A vector in a n-dimensional space. * * * \******************************************************************************/ #ifndef _ABBOTT_VECTOR_H_ #define _ABBOTT_VECTOR_H_ #include #include namespace Abbott { /** A vector in a n-dimensional space. * @param D The number of dimensions of this vector. * @param T The type used to store each vector component. */ template class Vector { public: /// Provides access to the vector's components T& operator[](int i) { return data_[i]; } const T& operator[](int i) const { return data_[i]; } /// Returns the vector's length. T length() const; /** Returns the vector's squared length. This is faster than calling * \textt{length()} and squaring it. */ T squaredLength() const; /// Normalizes the vector. void normalize(); /** Returns a normalized vector that "points" to same direction of * this. */ Vector normalized() const; // TODO: What should += and /= return? /// Adds a \textt{Vector} to this one. Vector operator+=(const Vector& other); /// Divides this \textt{Vector} by a scalar. Vector operator/=(T other); // Most compilers complain when this is private and the user tries to // initialize a vector like 'Vector<3> v = { 1.0, 0.5, 1.0 }'. T data_[D]; }; // - Vector::length() -------------------------------------------------- template T Vector::length() const { using namespace std; return sqrt(squaredLength()); } // - Vector::squaredLength() ------------------------------------------- template T Vector::squaredLength() const { T acc = 0.0; for (int i = 0; i < D; ++i) acc += data_[i] * data_[i]; return acc; } // - Vector::normalize() ----------------------------------------------- template void Vector::normalize() { T len = length(); for (int i = 0; i < D; ++i) data_[i] /= len; } // - Vector::normalized() ---------------------------------------------- template Vector Vector::normalized() const { Vector v(*this); v.normalize(); return v; } // - Vector::operator+= ------------------------------------------------ template Vector Vector::operator+=(const Vector& other) { for (int i = 0; i < D; ++i) data_[i] += other[i]; return *this; } // - Vector::operator/= ------------------------------------------------ template Vector Vector::operator/=(T other) { for (int i = 0; i < D; ++i) data_[i] /= other; return *this; } /** Adds two \texttt{Vector}s. * @param v1 The first \texttt{Vector}. * @param v2 The second \texttt{Vector}. * @param \texttt{v1} + \texttt{v2}. */ template Vector operator-(const Vector& v1, const Vector& v2) { Vector ret; for (int i = 0; i < D; ++i) ret[i] = v1[i] - v2[i]; return ret; } /** Subtracts two \texttt{Vector}s. * @param v1 The first \texttt{Vector}. * @param v2 The second \texttt{Vector}. * @param \texttt{v1} + \texttt{v2}. */ template Vector operator+(const Vector& v1, const Vector& v2) { Vector ret; for (int i = 0; i < D; ++i) ret[i] = v1[i] + v2[i]; return ret; } /** Divides a \texttt{Vector} by a scalar. * @param lhs The \texttt{Vector}. * @param rhs The scalar. * @return \texttt{lhs} / \texttt{rhs}. */ template Vector operator/(const Vector& lhs, T rhs) { Vector ret; for (int i = 0; i < D; ++i) ret[i] = lhs[i] / rhs; return ret; } /** Multiplies a \texttt{Vector} by a scalar. * @param lhs The \texttt{Vector}. * @param rhs The scalar. * @return \texttt{lhs} * \texttt{rhs}. */ template Vector operator*(const Vector& lhs, T rhs) { Vector ret; for (int i = 0; i < D; ++i) ret[i] = lhs[i] * rhs; return ret; } /** Multiplies a scalar by a \texttt{Vector}. * @param lhs The scalar. * @param rhs The \texttt{Vector}. * @return \texttt{lhs} * \texttt{rhs}. */ template Vector operator*(T lhs, const Vector& rhs) { Vector ret; for (int i = 0; i < D; ++i) ret[i] = lhs * rhs[i]; return ret; } /// Outputs a \texttt{Vector} to a \texttt{std::ostream}. template std::ostream& operator<<(std::ostream& lhs, const Vector& rhs) { lhs << '[' << rhs[0]; for (int i = 1; i < D; ++i) lhs << ", " << rhs[i]; lhs << ']'; return lhs; } /** Calculates the cross product between two \texttt{Vector}s. * @param v1 The first \texttt{Vector}. * @param v2 The second \texttt{Vector}. * @return The dot product between \texttt{vc1} and \texttt{vc2}. */ // TODO: Generalize to n-dimensions template Vector<3,T> CrossProd(const Vector<3,T>& v1, const Vector<3,T>& v2) { Vector<3,T> ret; ret[0] = (v1[1] * v2[2]) - (v1[2] * v2[1]); ret[1] = (v1[2] * v2[0]) - (v1[0] * v2[2]); ret[2] = (v1[0] * v2[1]) - (v1[1] * v2[0]); return ret; } /** Calculates the distance between between two \texttt{Vector}s. * @param v1 The first \texttt{Vector}. * @param v2 The second \texttt{Vector}. * @return The distance \texttt{vc1} and \texttt{vc2}. */ template T Distance(const Vector& v1, const Vector& v2) { T ret = 0; for (int i = 0; i < D; ++i) ret += (v1[i] - v2[i]) * (v1[i] - v2[i]); using namespace std; return sqrt(ret); } } // namespace Abbott #endif // _ABBOTT_VECTOR_H_ Geri/Abbott/Point.h0100644000175200001440000000272307617520454013133 0ustar lmbusers/******************************************************************************\ * Abbott, a library of usefull math stuff * * The library is named after Edwin A. Abbott (01828--01926), author of the * * great book "Flatland: A Romance of Many Dimensions". * * * * By Leandro Motta Barros * * * ******************************************************************************** * * * Point * * A point in a n-dimensional space. * * * \******************************************************************************/ #ifndef _ABBOTT_POINT_H_ #define _ABBOOT_POINT_H_ #include "Vector.h" /** The \textt{Point} is just an alias for \texttt{Vector}, but its use is * preferred whenever it makes the code clearer. Currently it is implemented as * a \texttt{#define}, which is quite error-prone. A \texttt{typedef} would be * better, but \texttt{typedef}s and template classes don't mix very well. */ #define Point Vector #endif // _ABBOT_POINT_H_ Geri/Abbott/Polygon.h0100644000175200001440000000547607637411414013476 0ustar lmbusers/******************************************************************************\ * Abbott, a library of usefull math stuff * * The library is named after Edwin A. Abbott (01828--01926), author of the * * great book "Flatland: A Romance of Many Dimensions". * * * * By Leandro Motta Barros * * * ******************************************************************************** * * * Polygon * * A polygon in a n-dimensional space. * * * \******************************************************************************/ // FIXME: Perhaps this is a good class to have, but it is not yet ready to use. namespace Abbott { /** A polygon in a n-dimensional space. * @param D The number of dimensions in the space where this polygon lives * @param T The type used to store */ // - Face ---------------------------------------------------------------- class Face { typedef std::vector::const_iterator ULConstIter; public: void addVertex(unsigned long v) { vertices_.push_back(v); } void addNormal(unsigned long n) { normals_.push_back(n); } void clear() { vertices_.clear(); normals_.clear(); } void render() const { assert(vertices_.size() == normals_.size()); Normal normal; if (normals_[0] == 0) { CalcNormal(Vertices[vertices_[0]], Vertices[vertices_[1]], Vertices[vertices_[2]], normal); } glBegin(GL_POLYGON); for (unsigned long i = 0; i < vertices_.size(); ++i) { if (normals_[i] != 0) glNormal3f(Normals[normals_[i]][0], Normals[normals_[i]][1], Normals[normals_[i]][2]); else glNormal3f(normal[0], normal[1], normal[2]); glVertex3f(Vertices[vertices_[i]][0], Vertices[vertices_[i]][1], Vertices[vertices_[i]][2]); } glEnd(); } private: std::vector vertices_; std::vector normals_; }; } // namespace Abbott