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