1 /************************************************************************* 2 * * 3 * Vega FEM Simulation Library Version 3.1 * 4 * * 5 * "objMesh" library , Copyright (C) 2007 CMU, 2009 MIT, 2016 USC * 6 * All rights reserved. * 7 * * 8 * Code authors: Jernej Barbic, Christopher Twigg, Daniel Schroeder, * 9 * Yili Zhao, Yijing Li * 10 * http://www.jernejbarbic.com/code * 11 * * 12 * Research: Jernej Barbic, Fun Shing Sin, Daniel Schroeder, * 13 * Doug L. James, Jovan Popovic * 14 * * 15 * Funding: National Science Foundation, Link Foundation, * 16 * Singapore-MIT GAMBIT Game Lab, * 17 * Zumberge Research and Innovation Fund at USC * 18 * * 19 * This library is free software; you can redistribute it and/or * 20 * modify it under the terms of the BSD-style license that is * 21 * included with this library in the file LICENSE.txt * 22 * * 23 * This library is distributed in the hope that it will be useful, * 24 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the file * 26 * LICENSE.TXT for more details. * 27 * * 28 *************************************************************************/ 29 30 #ifndef _OBJMESH_H_ 31 #define _OBJMESH_H_ 32 33 #include <math.h> 34 #include <string> 35 #include <vector> 36 #include <list> 37 #include <map> 38 #include <iostream> 39 #include <algorithm> 40 #include <assert.h> 41 #include <exception> 42 #include "minivector.h" 43 44 /* 45 This class stores a 3D surface mesh, loaded from an .obj file. 46 It makes it possible to access the mesh geometric primitives and perform 47 various geometric calculations and operations on the mesh. 48 49 A quick summary of the obj format: 50 1. vertices, normals, and texture coordinates are all specified in 51 a global 1-based namespace. 52 2. faces are divided into groups 53 3. each face consists of a listing of vertices, like this: 54 f 1/1/1 2/2/2 3/3/3 55 These numbers are references to the vertices, normals, and texture 56 coordinates, all of which were specified (as mentioned above) in 57 a global 1-based namespace. The values can be negative. A value of -1 58 means the *last* vertex, -2 is next-to-last vertex and so on. 59 60 To access a group/face/vertex from the ObjMesh file once it has been constructed, do the following: 61 1. Get the list of groups out of the .obj file using the "getGroupNames" function. 62 2. Select the group you want, and retrieve it using the "getGroup" function. 63 3. Iterate through the faces in the group using the "getFace" member function of the Group class. 64 4. Iterate through the vertices using the "getVertex" member function of the Face class. 65 5. Retrieve the various indices for the position, texture coordinate and normal of the vertex, 66 using the member functions of the Vertex class. 67 6. Look these up in the .obj file global namspace using the "getPosition", 68 "getTextureCoordinate", and "getNormal" member functions of the ObjMesh class. 69 70 Code authors: Jernej Barbic, Christopher Twigg, Daniel Schroeder, 71 CMU, 2001-2007, MIT 2007-2009, USC 2009-2011 72 */ 73 74 // thrown by the ObjMesh constructor 75 class ObjMeshException : public std::exception 76 { 77 public: 78 ObjMeshException(const std::string & reason); 79 ObjMeshException(const std::string & reason, const std::string & filename, unsigned int line); ~ObjMeshException()80 virtual ~ObjMeshException() throw() {} getReason()81 const std::string & getReason() const throw() { return reason; } what()82 virtual const char * what() const throw() { return reason.c_str(); } 83 84 protected: 85 std::string reason; 86 }; 87 88 class ObjMesh 89 { 90 public: 91 92 typedef enum {ASCII, BINARY, NUM_FILE_FORMATS} fileFormatType; 93 typedef enum {FILE_STREAM, MEMORY_STREAM} streamType; 94 95 // ======= member classes ======= 96 97 class Vertex 98 { 99 public: Vertex(const unsigned int & positionIndex_)100 explicit Vertex(const unsigned int & positionIndex_) 101 : positionIndex(positionIndex_), textureIndex(std::make_pair(false, 0)), normalIndex(std::make_pair(false, 0)) {} 102 Vertex(const unsigned int & positionIndex_,const unsigned int & textureIndex_)103 explicit Vertex(const unsigned int & positionIndex_, const unsigned int & textureIndex_) 104 : positionIndex(positionIndex_), textureIndex(std::make_pair(true, textureIndex_)), normalIndex(std::make_pair(false, 0)) {} 105 Vertex(const unsigned int & positionIndex_,const unsigned int & textureIndex_,const unsigned int & normalIndex_)106 explicit Vertex(const unsigned int & positionIndex_, const unsigned int & textureIndex_, const unsigned int & normalIndex_) 107 : positionIndex(positionIndex_), textureIndex(std::make_pair(true, textureIndex_)), normalIndex(std::make_pair(true, normalIndex_)) {} 108 Vertex(const unsigned int & positionIndex_,const std::pair<bool,unsigned int> textureIndex_,const std::pair<bool,unsigned int> normalIndex_)109 explicit Vertex(const unsigned int & positionIndex_, const std::pair<bool, unsigned int> textureIndex_, const std::pair<bool, unsigned int> normalIndex_) 110 : positionIndex(positionIndex_), textureIndex(textureIndex_), normalIndex(normalIndex_) {} 111 getPositionIndex()112 inline unsigned int getPositionIndex() const { return positionIndex; } getNormalIndex()113 inline unsigned int getNormalIndex() const { assert(normalIndex.first); return normalIndex.second; } getTextureCoordinateIndex()114 inline unsigned int getTextureCoordinateIndex() const { assert(textureIndex.first); return textureIndex.second; } getTextureIndexPair()115 inline std::pair< bool, unsigned int > getTextureIndexPair() const { return textureIndex; } getNormalIndexPair()116 inline std::pair< bool, unsigned int > getNormalIndexPair() const { return normalIndex; } 117 118 // Normals and texture coordinates are not considered "required" in the 119 // obj file format standard. Check these before retrieving them. hasNormalIndex()120 inline bool hasNormalIndex() const { return normalIndex.first; } hasTextureCoordinateIndex()121 inline bool hasTextureCoordinateIndex() const { return textureIndex.first; } 122 setPositionIndex(unsigned int positionIndex_)123 inline void setPositionIndex(unsigned int positionIndex_) { positionIndex = positionIndex_; } setNormalIndex(unsigned int normalIndex_)124 inline void setNormalIndex(unsigned int normalIndex_) { normalIndex = std::pair<bool,unsigned int>(true, normalIndex_); } setTextureCoordinateIndex(unsigned int textureCoordinate_)125 inline void setTextureCoordinateIndex(unsigned int textureCoordinate_) { textureIndex = std::pair<bool,unsigned int>(true, textureCoordinate_); } 126 127 protected: 128 unsigned int positionIndex; 129 std::pair< bool, unsigned int > textureIndex; 130 std::pair< bool, unsigned int > normalIndex; 131 }; 132 133 class Material 134 { 135 public: 136 explicit Material(const std::string name_, const Vec3d & Ka_, const Vec3d & Kd_, const Vec3d & Ks_, double shininess_=0, const std::string textureFilename_=std::string()): Ka(Ka_)137 Ka(Ka_), Kd(Kd_), Ks(Ks_), shininess(shininess_), alpha(1.0), name(name_), textureFilename(textureFilename_) {} 138 Material()139 explicit Material(): Ka(Vec3d(1,1,1)), Kd(Vec3d(1,1,1)), Ks(Vec3d(1,1,1)), shininess(0), alpha(1.0), name(std::string("default")), textureFilename(std::string()) {} 140 getName()141 inline std::string getName() const { return name; } getKa()142 inline const Vec3d & getKa() const { return Ka; } getKd()143 inline const Vec3d & getKd() const { return Kd; } getKs()144 inline const Vec3d & getKs() const { return Ks; } getShininess()145 inline double getShininess() const { return shininess; } getAlpha()146 inline double getAlpha() const { return alpha; } 147 setName(const std::string & name_)148 inline void setName(const std::string & name_) { name = name_; } setKa(const Vec3d & Ka_)149 inline void setKa(const Vec3d & Ka_) { Ka = Ka_; } setKd(const Vec3d & Kd_)150 inline void setKd(const Vec3d & Kd_) { Kd = Kd_; } setKs(const Vec3d & Ks_)151 inline void setKs(const Vec3d & Ks_) { Ks = Ks_; } setShininess(double shininess_)152 inline void setShininess(double shininess_) { shininess = shininess_; } setAlpha(double alpha_)153 inline void setAlpha(double alpha_) { alpha = alpha_; } setTextureFilename(const std::string & textureFilename_)154 inline void setTextureFilename(const std::string & textureFilename_) { textureFilename = textureFilename_; } setTextureFilename(const char * textureFilename_)155 inline void setTextureFilename(const char * textureFilename_) { textureFilename = std::string(textureFilename_); } 156 hasTextureFilename()157 inline bool hasTextureFilename() const { return (textureFilename.size() > 0); } getTextureFilename()158 inline std::string getTextureFilename() const { return textureFilename; } 159 160 bool operator==(const Material & material2) const; 161 162 protected: 163 Vec3d Ka, Kd, Ks; 164 double shininess; 165 double alpha; 166 std::string name; 167 std::string textureFilename; 168 }; 169 170 class Face 171 { 172 public: Face()173 explicit Face() : faceNormal(std::make_pair(false, Vec3d())) { vertices.reserve(3); } Face(const Vertex & v1,const Vertex & v2,const Vertex & v3)174 explicit Face(const Vertex & v1, const Vertex & v2, const Vertex & v3) : faceNormal(std::make_pair(false, Vec3d())) 175 { 176 vertices.reserve(3); 177 vertices.push_back(v1); 178 vertices.push_back(v2); 179 vertices.push_back(v3); 180 } 181 getNumVertices()182 inline size_t getNumVertices() const { return vertices.size(); } 183 // get vertex pointer. Warning: This pointer will be invalided if vertices are modified by Face::removeVertex(), Face::reverseVertices() or Face::addVertex() due to vector reallocation getVertex(unsigned int vertex)184 inline const Vertex & getVertex(unsigned int vertex) const { return vertices[vertex]; } getVertexHandle(unsigned int vertex)185 inline const Vertex * getVertexHandle(unsigned int vertex) const { return &(vertices[vertex]); } 186 setFaceNormal(const Vec3d & normal)187 inline void setFaceNormal(const Vec3d & normal) { faceNormal = std::pair<bool, Vec3d>(true, normal); } hasFaceNormal()188 inline bool hasFaceNormal() const { return faceNormal.first; }; getFaceNormal()189 inline const Vec3d & getFaceNormal() const { assert(faceNormal.first); return faceNormal.second; } 190 addVertex(const Vertex & v)191 inline void addVertex(const Vertex & v) { vertices.push_back(v); } removeVertex(unsigned int i)192 inline void removeVertex(unsigned int i) { vertices.erase(vertices.begin() + i); } reverseVertices()193 inline void reverseVertices() { reverse(vertices.begin(), vertices.end()); } printVertices()194 inline void printVertices() const { for(unsigned int i=0; i<vertices.size(); i++) std::cout << vertices[i].getPositionIndex() << " "; } 195 196 protected: 197 std::vector< Vertex > vertices; 198 std::pair< bool, Vec3d > faceNormal; 199 }; 200 201 class Group 202 { 203 public: 204 explicit Group(const std::string & name_, unsigned int materialIndex_=0) name(name_)205 : name(name_), materialIndex(materialIndex_) {} 206 getNumFaces()207 inline size_t getNumFaces() const { return faces.size(); } 208 // get face pointer. Warning: This pointer will be invalided if faces are modified by Group::removeFace() or ObjMesh::addFace() due to vector reallocation getFace(unsigned int face)209 inline const Face & getFace(unsigned int face) const { return faces[face]; } getFaceHandle(unsigned int face)210 inline const Face * getFaceHandle(unsigned int face) const { return &(faces[face]); } getFaceHandle(unsigned int face)211 inline Face * getFaceHandle(unsigned int face) { return &(faces[face]); } getName()212 inline const std::string & getName() const { return name; } setName(const std::string & name_)213 void setName(const std::string & name_) { name = name_; } getMaterialIndex()214 inline unsigned int getMaterialIndex() const { return materialIndex; } setMaterialIndex(unsigned int materialIndex_)215 inline void setMaterialIndex(unsigned int materialIndex_) { materialIndex = materialIndex_; } 216 addFace(const Face & face)217 inline void addFace(const Face & face) { faces.push_back(face); } reverseFace(unsigned int face)218 inline void reverseFace(unsigned int face) { faces[face].reverseVertices(); } 219 void removeFace(unsigned int face); 220 221 protected: 222 std::string name; 223 unsigned int materialIndex; 224 std::vector< Face > faces; 225 }; 226 227 // ======= constructors ======= 228 229 // Constructs the OBJ file and reads it in. Throws an ObjMeshException if it fails for any reason (file not there, etc.). 230 explicit ObjMesh(const std::string & filename, fileFormatType fileFormat = ASCII, int verbose = 0); 231 232 // makes an empty structure ObjMesh()233 explicit ObjMesh() {} 234 235 // creates a triangle mesh with a single group 236 explicit ObjMesh(int numVertices, const double * vertices, int numTriangles, const int * triangles); 237 238 // creates a mesh with a single group 239 explicit ObjMesh(int numVertices, const double * vertices, int numFaces, const int* faceVertexCounts, const int * faces); 240 241 // copy constructor 242 explicit ObjMesh(const ObjMesh & objMesh); 243 244 // advanced usage: 245 // stream is usually FILE_STREAM 246 explicit ObjMesh(void * binaryInputStream, streamType stream, int verbose = 0); 247 248 // ======= basic mesh info / stats ======= 249 getNumVertices()250 inline size_t getNumVertices() const { return vertexPositions.size(); } 251 unsigned int getNumFaces() const; // total number of faces in all groups getNumNormals()252 inline size_t getNumNormals() const { return normals.size(); } getNumTextureCoordinates()253 inline size_t getNumTextureCoordinates() const { return textureCoordinates.size(); } getNumGroups()254 inline size_t getNumGroups() const { return groups.size(); } getNumMaterials()255 inline size_t getNumMaterials() const { return materials.size(); } 256 // retrieve a list of all the group names in the obj file. 257 std::vector<std::string> getGroupNames() const; 258 // the filename from which this obj mesh was loaded (if it was loaded) getFilename()259 inline const std::string & getFilename() const { return filename; } 260 261 // prints info on the obj model 262 void printInfo() const; 263 264 // ======= member data getters / setters ======= 265 266 // all locations are 0-indexed getVertexIndex(unsigned int group,unsigned int face,unsigned int vertex)267 inline int getVertexIndex(unsigned int group, unsigned int face, unsigned int vertex) const { return groups[group].getFace(face).getVertex(vertex).getPositionIndex(); } // returns the global integer index of a specified group/face/vertex vertex 268 getPosition(int vertexIndex)269 inline const Vec3d & getPosition(int vertexIndex) const { return vertexPositions[vertexIndex]; } getPosition(const Vertex & vertex)270 inline const Vec3d & getPosition(const Vertex & vertex) const { return vertexPositions[vertex.getPositionIndex()]; } getTextureCoordinate(int textureCoordinateIndex)271 inline const Vec3d & getTextureCoordinate(int textureCoordinateIndex) const { return textureCoordinates[textureCoordinateIndex]; } getTextureCoordinate(const Vertex & vertex)272 inline const Vec3d & getTextureCoordinate(const Vertex & vertex) const { return textureCoordinates[vertex.getTextureCoordinateIndex()]; } getNormal(int normalIndex)273 inline const Vec3d & getNormal(int normalIndex) const { return normals[normalIndex]; } getNormal(const Vertex & vertex)274 inline const Vec3d & getNormal(const Vertex & vertex) const { return normals[vertex.getNormalIndex()]; } 275 setPosition(int vertexIndex,const Vec3d & position)276 inline void setPosition(int vertexIndex, const Vec3d & position) { vertexPositions[vertexIndex] = position; } setPosition(Vertex & vertex,const Vec3d & position)277 inline void setPosition(Vertex & vertex, const Vec3d & position) { vertexPositions[vertex.getPositionIndex()] = position; } setTextureCoordinate(int textureCoordinateIndex,const Vec3d & textureCoordinate)278 inline void setTextureCoordinate(int textureCoordinateIndex, const Vec3d & textureCoordinate) { textureCoordinates[textureCoordinateIndex] = textureCoordinate; } setTextureCoordinate(Vertex & vertex,const Vec3d & textureCoordinate)279 inline void setTextureCoordinate(Vertex & vertex, const Vec3d & textureCoordinate) { textureCoordinates[vertex.getTextureCoordinateIndex()] = textureCoordinate; } setNormal(int normalIndex,const Vec3d & normal)280 inline void setNormal(int normalIndex, const Vec3d & normal) { normals[normalIndex] = normal; } setNormal(Vertex & vertex,const Vec3d & normal)281 inline void setNormal(Vertex & vertex, const Vec3d & normal) { normals[vertex.getNormalIndex()] = normal; } 282 283 Group getGroup(const std::string name) const; // retrieve a group by its name 284 unsigned int getGroupIndex(const std::string name) const; // obtain a group index by its name 285 // get group pointer. Warning: This pointer will be invalided if groups are modified by ObjMesh::removeGroup() or ObjMesh::addGroup() due to vector reallocation getGroupHandle(unsigned int groupIndex)286 inline const Group * getGroupHandle(unsigned int groupIndex) const { return &(groups[groupIndex]); } getGroupHandle(unsigned int groupIndex)287 inline Group * getGroupHandle(unsigned int groupIndex) { return &(groups[groupIndex]); } 288 getMaterial(unsigned int materialIndex)289 inline const Material & getMaterial(unsigned int materialIndex) const { return materials[materialIndex]; } 290 unsigned int getMaterialIndex(const std::string name) const; // obtain a material index by its name 291 // get material pointer. Warning: This pointer will be invalided if materials are modified by ObjMesh::addMaterial() due to vector reallocation getMaterialHandle(unsigned int materialIndex)292 inline const Material * getMaterialHandle(unsigned int materialIndex) const { return &materials[materialIndex]; } getMaterialHandle(unsigned int materialIndex)293 inline Material * getMaterialHandle(unsigned int materialIndex) { return &materials[materialIndex]; } 294 void setMaterialAlpha(double alpha); 295 void setSingleMaterial(const Material & material); // erases all materials and sets a single material for the entire mesh 296 int usesTextureMapping(); // 0 = no group uses a material that references a texture image, 1 = otherwise 297 298 // ======= member data adders ======= 299 300 void addDefaultMaterial(); addMaterial(const Material & material)301 inline void addMaterial(const Material & material) { materials.push_back(material); } 302 inline void addMaterial(const std::string & name, const Vec3d & Ka, const Vec3d & Kd, const Vec3d & Ks, double shininess, const std::string textureFilename=std::string()) { materials.push_back(Material(name, Ka, Kd, Ks, shininess, textureFilename));} addGroup(const Group & group)303 inline void addGroup(const Group & group) { groups.push_back(group);} addGroup(const std::string & name)304 inline void addGroup(const std::string & name) { groups.push_back(Group(name));} 305 void removeGroup(const int groupIndex); 306 void removeGroup(const std::string name); 307 void removeAllGroups(); addVertexPosition(const Vec3d & pos)308 inline void addVertexPosition (const Vec3d & pos) { vertexPositions.push_back(pos); } addVertexNormal(const Vec3d & normal)309 inline void addVertexNormal (const Vec3d & normal) { normals.push_back(normal); } addTextureCoordinate(const Vec3d & textCoord)310 inline void addTextureCoordinate (const Vec3d & textCoord) { textureCoordinates.push_back(textCoord); } addFaceToGroup(const Face & face,unsigned int group)311 inline void addFaceToGroup(const Face & face, unsigned int group) { groups[group].addFace(face); } 312 313 // ======= optional member data setters ======= 314 315 // used to set values that are not filled upon construction 316 317 void computePseudoNormals(); // vertex pseudonormals getPseudoNormal(unsigned int i)318 inline const Vec3d & getPseudoNormal(unsigned int i) const { return pseudoNormals[i]; } // must first call "computePseudoNormals" 319 320 void computeEdgePseudoNormals(); // assumes that the faces are oriented coherently 321 int getEdgePseudoNormal(unsigned int i, unsigned int j, Vec3d * pseudoNormal) const; // must first call "computeEdgePseudoNormals" 322 323 void buildVertexFaceNeighbors(); 324 void clearVertexFaceNeighbors(); 325 void buildVertexNormals(double angle); // must generate vertex face neighbors and face normals first 326 void buildVertexNormalsFancy(double angle); // must generate vertex face neighbors and face normals first 327 328 void computeSurfaceAreaPerVertex(); getSurfaceAreaPerVertex(unsigned int i)329 inline double getSurfaceAreaPerVertex(unsigned int i) const { return surfaceAreaPerVertex[i]; } // must first call "computeSurfaceAreaPerVertex" 330 331 void initSurfaceSampling(); 332 Vec3d getSurfaceSamplePosition(double sample) const; // sample should be between 0 and 1; must call "initSurfaceSampling" first 333 334 // allows one to query the vertex indices of each triangle 335 // order of triangles is same as in "exportGeometry": for every group, traverse all faces, and tesselate each face into triangles 336 void initTriangleLookup(); // call this first 337 void clearTriangleLookup(); 338 void getTriangle(int triangleIndex, int * vtxA, int * vtxB, int * vtxC); // must call "initTriangleLookup" first 339 340 // ======= geometric queries ======= 341 342 bool isTriangularMesh() const; 343 bool isQuadrilateralMesh() const; 344 345 int computeNumIsolatedVertices() const; 346 unsigned int computeMaxFaceDegree() const; 347 348 double computeMinEdgeLength() const; // computes minimum edge length in the mesh 349 double computeMedianEdgeLength() const; // computes median edge length in the mesh 350 double computeAverageEdgeLength() const; // computes average edge length in the mesh 351 double computeMaxEdgeLength() const; // computes maximum edge length in the mesh 352 353 double computeMinEdgeLength(int * vtxa, int * vtxb) const; // also returns the two 0-indexed vertices achieving min 354 double computeMaxEdgeLength(int * vtxa, int * vtxb) const; // also returns the two 0-indexed vertices achieving max 355 356 // computes the 3D volume enclosed by the orientable surface 357 // assumes a triangle mesh 358 double computeVolume() const; 359 360 // the tighest fitting box is scaled by "expansionRatio" 361 // expansionRatio of 1 gives a tight-fitting bounding box 362 void getBoundingBox(double expansionRatio, Vec3d * bmin, Vec3d * bmax) const; // sides of the box may not be equal to each other 363 void getCubicBoundingBox(double expansionRatio, Vec3d * bmin, Vec3d * bmax) const; // forces a cubic bounding box 364 double getDiameter() const; // of the tight bounding box (expansion ratio = 1) 365 366 void getMeshRadius(const Vec3d & centroid, double * radius) const; 367 void getMeshGeometricParameters(Vec3d * centroid, double * radius) const; 368 369 void exportGeometry(int * numVertices, double ** vertices, int * numTriangles = NULL, int ** triangles = NULL, 370 int * numGroups = NULL, int ** triangleGroups = NULL) const; // all faces are triangulated before exporting 371 void exportFaceGeometry(int * numVertices, double ** vertices, int * numFaces = NULL, int ** faceCardinalities = NULL, int ** faces = NULL, 372 int * numGroups = NULL, int ** faceGroups = NULL) const; // faces are not triangulated before exporting 373 void exportUVGeometry(int * numUVVertices, double ** UVVertices, int * numUVTriangles, int ** UVTriangles) const; // exports the geometry in the texture coordinate space 374 375 Vec3d computeFaceCentroid(const Face & face) const; 376 double computeFaceSurfaceArea(const Face & face) const; // of a single face 377 void computeFaceSurfaceAreas(std::vector<double> & surfaceAreas) const; // of all faces 378 379 // warning: the normal is computed based on the first three vertices in a face (assumes planar face): 380 Vec3d computeFaceNormal(const Face & face) const; // using a cross-product of face edges; does not modify "face" 381 void buildFaceNormals(); // builds face normals for all the faces (and writes them internally to each Face object) 382 383 double computeMass(const std::vector<double> & groupSurfaceMassDensities) const; // of the entire mesh; second argument gives the surface mass density for each group; its length must equal the number of groups 384 Vec3d computeCenterOfMass_Vertices() const; // of the vertices 385 Vec3d computeCenterOfMass_Triangles() const; // of the triangular surface 386 387 Vec3d computeCenterOfMass_Triangles(const std::vector<double> & groupSurfaceMassDensities) const; // second argument gives the surface mass density for each group 388 389 void computeInertiaTensor_Triangles(double IT[6]) const; // of the triangular surface, with respect to the center of mass, assumes uniform mass density on the triangles = 1 390 void computeInertiaTensor_Triangles(double mass, double IT[6]) const; // of the triangular surface, with respect to the center of mass, assumes uniform density on the triangles, which is set such that the total object mass equals "mass" 391 void computeInertiaTensor_Triangles(const std::vector<double> & groupSurfaceMassDensities, double IT[6]) const; // // of the triangular surface, with respect to the center of mass, based on the given surface mass density for each group 392 393 static double computeTriangleSurfaceArea(Vec3d & p0, Vec3d & p1, Vec3d & p2); // compute surface area of a triangle 394 double computeSurfaceArea() const; // of the entire mesh 395 void computeSurfaceAreaPerGroup(std::vector<double> & surfaceAreas) const; // for each group separately 396 // computes masses "belonging" to each vertex, given the surface mass densities 397 void computeMassPerVertex(const std::vector<double> & groupSurfaceMassDensities, std::vector<double> & masses) const; 398 399 // finds the closest mesh vertex to the query position queryPos (using exhaustive search); also outputs distance to such a vertex (if distance is not NULL) 400 unsigned int getClosestVertex(const Vec3d & queryPos, double * distance=NULL) const; 401 402 void computeCentroids(std::vector<Vec3d> & centroids) const; // centroids of all the faces 403 void interpolateToCentroids(const std::vector<double> & nodalData, std::vector<double> & centroidData) const; // interpolates vertex data to centroids 404 void interpolateToCentroids(const std::vector<Vec3d> & nodalData, std::vector<Vec3d> & centroidData) const; // interpolates vertex data to centroids 405 406 // ======= mesh modification ======= 407 408 void triangulate(); 409 410 void scaleUniformly(const Vec3d & center, double factor); // scales the model uniformly, with center being the center of the scaling 411 void transformRigidly(const Vec3d & translation, const Mat3d & rotation); 412 void deform(double * u); 413 414 int removeDuplicatedMaterials(); 415 int removeIsolatedVertices(); // removes vertices that don't appear in any triangle 416 int removeIsolatedTextureCoordinates(); // removes texture coordinates that are not referenced 417 int removeIsolatedNormals(); // removes normals that are not referenced 418 int removeZeroAreaFaces(int verbose=0); 419 // removes faces that have an edge shared by two other faces AND an edge not shared by any other face (making the mesh more manifold) 420 // this function does one iteration of this process; you may need to call it again to continue removing faces, until the function returns 0 421 int removeHangingFaces(); 422 // collapses edges that are shared by more than two faces 423 // this function does one iteration of this process; you may need to call it again to continue removing faces, until the function returns 0 424 int removeNonManifoldEdges(); 425 void collapseEdge(unsigned int vertexA, unsigned int vertexB, int removeIsolatedVertices=1); // collapses the edge between vertices vertexA and vertexB 426 427 // generates vertex normals by averaging normals for adjacent faces 428 // any pre-specified normals are overwritten by these new normals 429 // does not assume a triangular mesh 430 void setNormalsToAverageFaceNormals(); 431 // sets vertex normals to face normals 432 void setNormalsToFaceNormals(); 433 // sets vertex normals to vertex pseudonormals 434 void setNormalsToPseudoNormals(); 435 436 void renumberVertices(const std::vector<int> & permutation); 437 438 // merges all the specified groups into a single group 439 // groupIndices need not be sorted 440 // the index of the merged group is set to the smallest index among "groupIndices" 441 void mergeGroups(const std::vector<int> & groupIndices); // 0-indexed 442 443 void removeEmptyGroups(); 444 445 void appendMesh(ObjMesh * mesh); // appends "mesh" to this mesh 446 447 // ======= mesh cloning (with modifications) ======= 448 449 // creates a cloned mesh, keeping the specified faces in groups 450 ObjMesh * clone(const std::vector<std::pair<int, int> > & groupsAndFaces, int removeIsolatedVertices=1) const; 451 452 // splits the mesh into groups, one per each connected component 453 // if withinGroupsOnly=0, splitting is global, which means that some groups may be fused into one bigger group 454 // if withinGroupsOnly=1, splitting is performed within each group only 455 ObjMesh * splitIntoConnectedComponents(int withinGroupsOnly=0, int verbose=0) const; 456 // extracts a specified group 457 ObjMesh * extractGroup(unsigned int group, int keepOnlyUsedNormals = 1, int keepOnlyUsedTextureCoordinates = 1) const; 458 459 // ======= file output ======= 460 461 // saves to an obj file (including saving materials to filename.mtl if outputMaterials=1) 462 void save(const std::string & filename, int outputMaterials=0, fileFormatType fileFormat = ASCII, int verbose=1) const; 463 464 static int saveObjMeshesToBinary(const std::string & binaryFilename, int numObjMeshes, ObjMesh ** objMeshes, int * saveObjMeshesFlag, int outputMaterials = 0, int verbose = 0); 465 466 // saves to a stl file (only saves geometry (not materials)) 467 void saveToStl(const std::string & filename) const; 468 469 // saves to a smesh file (only saves geometry (not materials)) 470 void saveToSmesh(const std::string & filename) const; 471 472 // format similar to Abaqus 473 // only writes geometry (not materials) 474 void saveToAbq(const std::string & filename) const; 475 476 // extracts directory name from a given path 477 static void dirname(const char * path, char * result); 478 479 // ======= multifile input ======== 480 481 // 0: succeeded 482 // 1: failed 483 static int loadObjMeshesFromBinary(const std::string & binaryFilename, int * numObjMeshes, ObjMesh *** objMeshes, int verbose = 0); 484 485 // ======= advanced usage ======= 486 487 // computes internal axis-aligned bounding box 488 void computeBoundingBox(); // sets diameter, bmin, bmax, center, cubeHalf 489 isNaN(double x)490 inline static bool isNaN(double x) { return Vec3d::isNaN(x); } 491 492 protected: 493 494 static int loadObjMeshesFromBinary(FILE * fin, int * numObjMeshes, ObjMesh *** objMeshes, int verbose = 0); 495 496 // ======= file load ======= 497 // return number of elments read from the memoryLocation 498 static unsigned int readFromMemory(void * buf, unsigned int elementSize, unsigned int numElements, void * memoryLocation); 499 static unsigned int readFromFile(void * buf, unsigned int elementSize, unsigned int numElements, void * fin); 500 int loadFromAscii(const std::string & filename, int verbose = 0); 501 int loadFromBinary(const std::string & filename, int verbose = 0); 502 int loadFromBinary(void * binaryInputStream, streamType stream = FILE_STREAM, int verbose = 0); 503 504 // ======= file save ======= 505 void saveToAscii(const std::string & filename, int outputMaterials=0, int verbose=1) const; 506 // saves obj and mtl together to a binary file 507 // return 0 if succeeded 508 int saveToBinary(const std::string & filename, int outputMaterials = 0, int verbose = 0) const; 509 int saveToBinary(FILE * binaryOutputStream, int outputMaterials = 0, unsigned int * bytesWritten = NULL, bool countBytesOnly = false, int verbose = 0) const; 510 511 std::vector< Material > materials; 512 std::vector< Group > groups; 513 std::vector< Vec3d > vertexPositions; 514 std::vector< Vec3d > textureCoordinates; 515 std::vector< Vec3d > normals; 516 std::string filename; 517 518 double diameter; 519 Vec3d bmin, bmax; 520 Vec3d center, cubeHalf; 521 522 std::vector<double> surfaceAreaPerVertex; 523 std::vector<Vec3d> pseudoNormals; 524 525 std::vector<int> triangles; // for triangle vertex lookup 526 527 // index assumes that the first int is smaller than the second 528 std::map< std::pair<unsigned int,unsigned int>, Vec3d > edgePseudoNormals; 529 530 // stores the information about a face that is adjacent to a vertex 531 class VertexFaceNeighbor 532 { 533 public: groupIndex(groupIndex_)534 explicit VertexFaceNeighbor(int groupIndex_, int faceIndex_, int faceVertexIndex_, bool averaged_=false) : groupIndex(groupIndex_), faceIndex(faceIndex_), faceVertexIndex(faceVertexIndex_), averaged(averaged_) {} 535 getGroupIndex()536 inline int getGroupIndex() const { return groupIndex; } getFaceIndex()537 inline int getFaceIndex() const { return faceIndex; } getFaceVertexIndex()538 inline int getFaceVertexIndex() const { return faceVertexIndex; } getAveraged()539 inline bool getAveraged() const { return averaged; } 540 setAveraged(bool averaged_)541 inline void setAveraged(bool averaged_) { averaged = averaged_; } 542 543 protected: 544 int groupIndex; //the group containing the face w/ the vertex position 545 int faceIndex; //the face containing the vertex position 546 int faceVertexIndex; //the index of the face vertex at this vertex position 547 bool averaged; //indicates if it was averaged 548 }; 549 550 std::vector<std::list<VertexFaceNeighbor> > vertexFaceNeighbors; 551 552 // inertia tensor around the origin, assuming the triangle has mass 1 553 void computeSpecificInertiaTensor(Vec3d & v0, Vec3d & v1, Vec3d & v2, double t[6]) const; 554 555 void parseMaterials(const std::string & objMeshname, const std::string & materialFilename, int verbose=1); 556 557 std::vector<std::pair<double, const Face*> > surfaceSamplingAreas; 558 559 static void removeWhitespace(char * s); 560 static void convertWhitespaceToSingleBlanks(char * s); 561 562 static void fgets_(char * s, int n, FILE * stream); 563 }; 564 565 #endif 566 567