Tutorial
From vcg Library
Contents |
What is VCG Lib ?
The VCG Lib could be defined as a library for managing triangle meshes, but that would be unfair. VCG Lib is a library for managing 0,1,2 and 3-simplicial complexes, which means Point Sets, Edge Mesh, Triangle Mesh and Tetrahedral Meshes. To be completely honest, it is starting to support polygon meshes as well (stay tuned).
It just happens that the research interests of the developers leaded to develop mesh processing algorithms mostly for triangle meshes, but the kernel is there waiting... However, to avoid annoying the reader with 0-simplices, maximal simplices and so on, in this tutorial we will always refer to the case of triangle meshes, extending the explanation to the other cases only where appropriate.
please note:this is not a reference manual, but a short tutorial to guide you through the few basic concepts of VCG Lib. The idea is that after reading this tutorial and trying the few examples in apps/samples/ referred in the following, you will have all it takes to write your own code.
Installation and folder structure
Getting VCG Lib
VCG Lib uses a SVN repository. To get the right subset of the svn trunk in the devel folder you should issue the following svn command:
svn co https://vcg.svn.sourceforge.net/svnroot/vcg/trunk/vcglib vcglib
Windows users with tortoisesvn (http://www.tortoisesvn.net) installed should
1. create a folder named vcglib. 2. right-click in the newly created vcglib folder and choose SVN Checkout 3. put in the first parameter: https://vcg.svn.sourceforge.net/svnroot/vcg/trunk/vcglib 4. press ok and wait, it should start to check out the last revision of the whole tree.
Folder Structure
- Main article: Folder structure
VCG Lib is mostly made of header files (and its core part it's only header files). Just download the tarball from here and uncompress it in a folder, e.g. named vcg, inside you compiler "include" directory. Afterwards, you will include the file you need.
Inside vcg folder you will find 4 sub-folders:
- vcg: this is the core of the library, where all the algorithms and data structures are defined. This part is pure, quite heavily templated, C++ code with STL support for common data structures and algorithms. You will not find a single
includefrom here to anything else than standard libraries. Note that core part is made of header files (.hfiles) only. - wrap: here you will find the wrapping of VCG concepts towards specific needs/contexts/libraries. For example you will find all the code to import/export meshes from the hard disk in many formats, or the routines for rendering triangle meshes with OpenGL, supports for common GUI tools like a trackball, and so on..
- apps: this folder contains the command line applications developed with the VCG Lib. Many (much more) examples can be found in MeshLab. The apps/simple directory contains a sub-collection of very basic apps. A good starting point for beginners!
- docs: documentation lives here (including this tutorial)
Basic Concepts
How to define a mesh type
The VCG Lib encodes a mesh as a set of vertices and triangles (i.e. triangles for triangle meshes, tetrahedra for tetrahedral meshes). The following line will be a part of the definition of a VCG type of mesh:
class MyMesh : public vcg::TriMesh< std::vector<MyVertex>, std::vector<MyFace> > {}
vcg::TriMesh is the base type for a triangle mesh and it is templated on:
- the type of STL container containing the vertices
- which in turn is templated on your vertex type
- the type of STL container containing the faces
- which in turn is templated on your face type
The face and the vertex type are the crucial bits to understand in order to be able to take the best from VCG Lib.
A vertex, an edge, a face and a tetrahedron are just an user defined (possibly empty) collection of attribute. For example you will probably expect MyVertex to contain the (x,y,z) position of the vertex, but what about the surface normal at the vertex?.. and the color? VCG Lib gives you a pretty elegant way to define whichever attributes you want to store in each vertex, face, or edge. For example, the following example shows three valid definitions of MyVertex :
#include <vcg/simplex/vertex/base.h>
#include <vcg/simplex/vertex/component.h>
class MyFace;
class MyEdge;
class MyVertex0 : public vcg::VertexSimp2<MyVertex0, MyEdge, MyFace,
vcg::vertex::Coord3d,
vcg::vertex::Normal3f> {};
class MyVertex1 : public vcg::VertexSimp2<MyVertex1, MyEdge, MyFace,
vcg::vertex::Coord3d,
vcg::vertex::Normal3f,
vcg::vertex::Color4b> {};
class MyVertex2 : public vcg::VertexSimp2<MyVertex2, MyEdge,MyFace> {};
vcg::VertexSimp2 is the VCG base class for a vertex belonging to a 2-simplex. If we were to define a tetraedral mesh, for example, we would have used vcg::VertexSimp3. The first 3 templates of vcg::VertexSimp2 must specify the type of all the simplicies involved in the mesh in ascending order: the vertex type itself, the type of edge and the type of triangle (and the type of tetrahedron for the tetrahedral meshes). It can be annoying when you see it but it is useful that every entity involved knows the type of the others and this is the way VCG Lib does it.
As you can see the three definitions of MyVertex differ for the remaining template parameters. These specify which attributes will be stored with the vertex type: MyVertex0 is a type storing coordinates as a triple of doubles and normal as a triple of floats, MyVertex1 also store a color value specified as 4 bytes and MyVertex2 does not store any attribute, is an empty class.
vcg::Coord3d, vcg::Normal3f, vcg::Color4b and many others are implemented in VCG, their complete list can be found here. You can place any combination of them as a template parameters of your vertex (your simplex) type.
Now we have all it takes for a working definition of MyMesh type:
/* apps/sample/trimesh_base/trimesh_definition.h */
#include <vector>
#include <vcg/simplex/vertex/base.h>
#include <vcg/simplex/vertex/component.h>
#include <vcg/simplex/face/base.h>
#include <vcg/simplex/face/component.h>
#include <vcg/complex/trimesh/base.h>
class MyEdge;
class MyFace;
class MyVertex : public vcg::VertexSimp2<MyVertex, MyEdge, MyFace,
vcg::vertex::Coord3d,
vcg::vertex::Normal3f> {};
class MyFace : public vcg::FaceSimp2<MyVertex, MyEdge, MyFace,
vcg::face::VertexRef> {};
class MyMesh : public vcg::tri::TriMesh<
std::vector<MyVertex>, std::vector<MyFace> > {};
int main()
{
MyMesh m;
return 0;
}
One more comment: vcg::VertexRef is an attribute that stores 3 pointers to the type of vertex, so implementing the Idexed Data structure. This is an example of why the type MyFace needs to know the type MyVertex
note: Although we left the STL type of container of vertices and faces as a template parameter, at the current state many kernel algorithms of VCG Lib assumes they are STL vector, so if you pass a std::list or a map your use of the library will be quite limited.
How to create a mesh
Once you declared your mesh type, you may want to instance an object and to fill it with vertexes and triangles. It may cross your mind that you could just make some push_back on the vertexes and faces container (data member vert and face of class vcg::tri::Trimesh). In fact this is the wrong way since there can be side effects by adding element to a container. We describe this issue and the correct way of adding mesh element in the Allocation page.
The flags of the mesh elements
Usually to each element of the mesh we associate a small bit vector containing useful single-bit information about vertices and faces. For example the deletion of vertex simply mark a the Deletion bit in thsi vector (more details on the various deletion/allocation issues in the Allocation page. More details on the various kind of flags that can be associated are in the Flags page.
How to process a mesh
The algorithms that do something on a mesh are generally written as static member functions of a class templated on the mesh type. For example the code snipped below is part of the class UpdateNormals, which contains the several algorithms to compute the value of the normal
/** vcg/complex/trimesh/update/normal.h */
...
template <class ComputeMeshType>
class UpdateNormals{
...
/// Calculates the vertex normal (if stored in the current face type)
static void PerFace(ComputeMeshType &m)
/// Calculates the vertex normal. Without exploiting or touching face normals
/// The normal of a vertex v is the weigthed average of the normals of the faces incident on v.
static void PerVertex(ComputeMeshType &m)
/// Calculates both vertex and face normals.
/// The normal of a vertex v is the weigthed average of the normals of the faces incident on v.
static void PerVertexPerFace(ComputeMeshType &m)
...
};
This class is part of a kernel of classes with name UpdateValue that compute the value of the vertex or face attributes and that can be found altogether in the folder vcg/complex/trimesh/update. For example, the following example show how to compute the value of the normal and the mean and gaussian curvature per vertex:
/* apps/sample/trimesh_base/trimesh_definition.h */
#include <vector>
#include <vcg/simplex/vertex/base.h>
#include <vcg/simplex/vertex/component.h>
#include <vcg/simplex/face/base.h>
#include <vcg/simplex/face/component.h>
#include <vcg/complex/trimesh/base.h>
#include <vcg/complex/trimesh/update/normals.h> //class UpdateNormals
#include <vcg/complex/trimesh/update/curvature.h> //class UpdateCurvature
class MyEdge;
class MyFace;
class MyVertex: public vcg::VertexSimp2<MyVertex,MyEdge,MyFace, vcg::vertex::Coord3d, vcg::vertex::Normal3f,vcg::vertex::Curvaturef>{};
class MyFace: public vcg::FaceSimp2<MyVertex,MyEdge,MyFace, vcg::face::VertexRef>{};
class MyMesh: public vcg::tri::TriMesh< std::vector<MyVertex>, std::vector<MyFace> > {};
int main()
{
MyMesh m;
// fill the mesh
...
// compute the normal per-vertex -> update the value of v.N() forall v (vcg::vertex::Normal3f)
vcg::tri::UpdateNormals<MyMesh>::PerVertexPerFace(m);
// compute the curvature per-vertex -> update the value of v.H() and v.K() (vcg::vertex::Curvaturef)
vcg::tri::UpdateCurvature<MyMesh>::MeanAndGaussian(m);
return 0;
}
Other than algorithms that update values of the mesh attributes, VCG Lib provides algorithms to create a mesh from another source, for example from point sets (by means of the Ball Pivoting apprach) or as isosurfaces from a volumetric dataset (by means of Marching Cubes algorithm). Those algorithm can be found in vcg/complex/trimesh/create/.
Finally, you can find algorithms for refinement (midpoint, Loop, Butterfly...), for smoothing, for closing holes and other that are not currently classified under any specific heading and that you can find under /vcg/complex/trimesh.
Optional Component
There are many cases where some vertex or face attributes are not necessary all the time. VCG Lib gives you a way to specify optional components, i.e. attributes that are not statically stored within the simplex but can be dynamically allocated when you need them. We use the term component to indicate that they are part of the simplex type and that there is a member function to access their value.
To 'define' optional component you need to do two things:
- to use a special type of container (derived from std::vector)
- to specify the right type of component in the template parameters
An optional component can be accessed when the memory for it has been allocated, in which case we say that the component is 'enabled'. An optional component can be enable by calling the function Enable.
VCG Lib handles optional components with two alternative mechanisms: the first is called 'Ocf' (for Optional Component Fast) which uses one pointer for each simplex but it makes accessing optional attribute almost as fast as non-optional ones; the second is called 'Occ' (for Optional Component Compact) which only requires a little extra space for each mesh (unrelated to its size in terms of number of simplicies) but which can result in a slower access. In the following, only their use is discussed. The implementation detail can be found here.
Optional Component Fast
The following definition of MyMesh specifies that the 'normal' component of the type MyVertex is optional. The differences from the previous example are shown in bold.
- include the header for the
optional_component_ocf.hwhich contains the definitions for the optional attributes; - include the special attribute <vcg::vertex::Info> as first attribute of the type
vcg::vertex::VertexSimp2; - use the type
vcg::vector_ocfas the container of your vertices; - include the type vcg::vertex::Normal3fOcf among the template parameters
/* check apps/sample/trimesh_optional/trimesh_optional.cpp */
#include <vector>
#include <vcg/simplex/vertex/base.h>
#include <vcg/simplex/vertex/component_ocf.h>
#include <vcg/simplex/face/base.h>
#include <vcg/simplex/face/component.h>
#include <vcg/complex/trimesh/base.h>
class MyEdge;
class MyFace;
class MyVertex: public vcg::VertexSimp2<MyVertex,MyEdge,MyFace,vcg::vertex::InfoOcf,vcg::vertex::Coord3d, vcg::vertex::Normal3fOcf>{};
class MyFace: public vcg::FaceSimp2<MyVertex,MyEdge,MyFace,vcg::face::VertexRef>{};
class MyMesh: public vcg::tri::TriMesh<vcg::vertex::vector_ocf<MyVertex>, std::vector<MyFace> >{};
int main()
{
MyMesh m;
// ...fill the mesh...
MyMesh::VertexIterator vi = m.vert.begin();
(*vi).N() = vcg::Point3f(1.0,1.0,1.0); // ERROR
m.vert.EnableNormal(); // this allocate the memory to store the normal
(*vi).N() = vcg::Point3f(1.0,1.0,1.0); // OK
m.vert.DisableNormal(); // this deallocate the memory to store the normal
(*vi).N() = vcg::Point3f(1.0,1.0,1.0); // ERROR (again)!
return 0;
}
Before accessing the data contained in the normal component, you must enable the attribute by calling EnableNormal(), afterwards the space for the component will be allocated and accessible until you call DisableNormal(). Trying to access the value of the normal before enabling or after disabling the corresponding component will throw an assertion.
Optional Component Compact
The following definition of MyMesh specifies that the 'normal' attribute of the type MyVertex is optional.
To use a Occ component you must:
- include
#include <vcg/simplex/vertex/component_occ.h; - use the type
vcg::vector_occas the container of your vertices. - specify the attribute normal as optional by passing
vcg::vertex::Normal3fOccamong template parameters ofVertexSimp2.
/* check apps/sample/trimesh_optional/trimesh_optional.cpp */
#include <vector>
#include <vcg/simplex/vertex/base.h>
#include <vcg/simplex/vertex/component_occ.h>
#include <vcg/simplex/face/base.h>
#include <vcg/simplex/face/component.h>
#include <vcg/complex/trimesh/base.h>
class MyEdge;
class MyFace;
class MyVertex: public vcg::VertexSimp2<MyVertex,MyEdge,MyFace, vcg::vertex::Coord3d, vcg::vertex::Normal3fOcc>{};
class MyFace: public vcg::FaceSimp2<MyVertex,MyEdge,MyFace,vcg::face::VertexRef>{};
class MyMesh: public vcg::tri::TriMesh< vcg::vertex::vector_occ<MyVertex>, std::vector<MyFace> >{};
int main()
{
MyMesh m;
// ...fill the mesh...
MyMesh::VertexIterator vi = m.vert.begin();
(*vi).N() = vcg::Point3f(1.0,1.0,1.0); // ERROR
m.vert.Enable<vcg::vertex::NormalType>(); // this allocate the memory to store the normal
(*vi).N() = vcg::Point3f(1.0,1.0,1.0); // OK
m.vert.Disable<vcg::vertex::NormalType>(); // this deallocate the memory to store the normal
(*vi).N() = vcg::Point3f(1.0,1.0,1.0); // ERROR (again)!
return 0;
}
Before accessing the data contained in the normal, you must enable the attribute by calling EnableAttribute<vcg::vertex::Normal3fOcc::NormalType>(), afterwards the attribute will be allocated and accessible until you call DisableAttribute<vcg::vertex::Normal3fOcc::NormalType>(). Trying to access the value of the normal before enabling or after disabling the corresponding attribute will throw an assertion.
Two important things about optional optional components:
- The access function to the value of a component is the same both if the component is optional and if it's not. This means that you do not have to make this distinction when coding your algorithm
- If you make a copy of the vertex(face) and then try to access a component it won't work. The mechanisms for optional attributes works using the position of the vertex(face) in memory. In other words, the optional data is associated with a position of the container (
vector_ocforvector_occ) and not bound to the vertex itself.
User-defined attributes
VCG Lib also provides a simple mechanism to associate user-defined 'attributes' to the simplicies and to the mesh.
Note that both 'attributes' and 'components' are basically accessory data that are bound to a simplex.
Conceptually the difference is that with the term component VCGLib indicates those values that are considered to 'define' the simplex (its position, its normal its connectivity information), while the user defined attribute is an accessory data which make sense to some specific algorithm, like "the number of time a vertex has been moved" or "a pointer to a string containing a description of the vertex".
Practically the difference is that every optional component has its non optional counterpart and
is accessed through a member function of the simplex, so that when you write
your algorithm you use v.N() to access the normal both it is has been declared as optional or not, while
the attributes are accessed by a handle which is returned at the creation of the attribute.
The following code snippet shows an example:
/* apps/sample/trimesh_attribute/trimesh_attribute.cpp */
#include<vcg/simplex/vertex/base.h>
#include<vcg/simplex/vertex/component.h>
#include<vcg/simplex/face/base.h>
#include<vcg/simplex/face/component.h>
#include<vcg/complex/trimesh/base.h>
#include<vcg/complex/trimesh/allocate.h>
class MyFace;
class MyVertex;
class MyEdge; // dummy prototype never used
class MyVertex : public vcg::VertexSimp2< MyVertex, MyEdge, MyFace, vcg::vertex::Coord3f,vcg::vertex::Normal3f>{};
class MyFace : public vcg::FaceSimp2< MyVertex, MyEdge, MyFace, vcg::face::VertexRef, vcg::face::Normal3f> {};
class MyMesh : public vcg::tri::TriMesh< std::vector<MyVertex>, std::vector<MyFace> > {};
float Irradiance(MyMesh::VertexType v){
// .....
return 1.0;
}
int main()
{
MyMesh m;
//...here m is filled
// add a per-vertex attribute with type float named "Irradiance"
MyMesh::PerVertexAttributeHandle<float> ih = vcg::tri::Allocator<MyMesh>::AddPerVertexAttribute<float> (m,std::string("Irradiance"));
// add a per-vertex attribute with type float named "Radiosity"
vcg::tri::Allocator<MyMesh>::AddPerVertexAttribute<float> (m,std::string("Radiosity"));
// add a per-vertex attribute with type bool and no name specified
MyMesh::PerVertexAttributeHandle<bool> blocked_h = vcg::tri::Allocator<MyMesh>::AddPerVertexAttribute<bool> (m);
MyMesh::VertexIterator vi; int i = 0;
for(vi = m.vert.begin(); vi != m.vert.end(); ++vi,++i){
ih[vi] = Irradiance(*vi); // [] operator takes a iterator
ih[*vi] = Irradiance(*vi); // or a MyMesh::VertexType object
ih[&*vi]= Irradiance(*vi); // or a pointer to it
ih[i] = Irradiance(*vi); // or an integer index
}
// Once created with AddPerVertexAttribute, an handle to the attribute can be obtained as follows
MyMesh::PerVertexAttributeHandle<float> rh = vcg::tri::Allocator<MyMesh>::GetPerVertexAttribute<float>(m,"Radiosity");
// you can query if an attribute is present or not
bool hasRadiosity = vcg::tri::HasPerVertexAttribute(m,"Radiosity");
// you can delete an attibute by name
vcg::tri::Allocator<MyMesh>::DeletePerVertexAttribute<float>(m,"Radiosity");
// you can delete an attibute by handle
vcg::tri::Allocator<MyMesh>::DeletePerVertexAttribute<bool>(m,blocked_h);
}
The same can be done for the faces, just replace the occurences of PerVertex with PerFace.
Note that if you call add an attribute without specifying a name and you lose the handle, you will not be able to get your handle back.
Note:
- Do not get mix up the scope of the handle with the memory allocation of the attribute. If you do not delete an attribute explicitly, it will be allocated until the mesh itself is destroyed, even if you do not have handles to it.
C++ type of a mesh and reflection
VCG Lib provides a set of functions to implement reflection, i.e. to investigate the type of a mesh at runtime.
These functions follow the format Has[attribute](mesh) and return a boolean stating if that particular attribute is present or not.
template<class ComputeMeshType>
static void UpdateNormals<ComputeMeshType>::PerVertex(ComputeMeshType &m)
{
if( !HasPerVertexNormal(m)) return;
...
}
You may wonder why those functions are not statically typed and why they needs the mesh object, i.e. why can't you just write ComputeMeshType::HasPerVertexNormal()? The reason is that VCG Lib reflection takes into account optional components, therefore HasPerVertexNormal(m) will return true if the type of the vertex contains the attribute as permanent (e.g. vcg::vertex::Normal3f) OR if it contains the attribute as optional (e.g. vcg::vertex::Normal3fOcf) AND it is enabled, i.e. the relative Enable function has been called.
Adjacency
VCG Lib does not have a hard-coded way to encode the adjacencies among simplices. It all depends on which attributes are stored with the simplices and how they are used. I the previous examples the definition of face has always included the attribute vcg::face::VertexRef , which stores 3 pointers to MyVertex accessible with the member function V() (the well known Indexed Data Structure). The reason is that almost all of the algorithms currently implemented in VCG Lib assume its presence. So, if your type MyFace does not include the attribute vcg::face::VertexRef, the definition will be correct but almost no algorithm will work..
There are other adjacency relations that can be useful to navigate a mesh, for example to collect the one-ring neighborhood of a vertex. VCG Lib uses two mechanisms which are explained in the following, along with the attributes they use.
FF Adjacency
The face-to-face adjacency, stored in the attribute for the faces vcg::face::FFAdj ( vcg::face::TTAdj for tetrahedra), encodes the adjacency of faces (tetrahedra) through edges (faces).
The image below shows two triangle faces with the convention adopted for indexing vertexes and edges.
The vertexes are numbered from 0 to 2 in CCW sense and the edge i = 0..2 is the edge whose extremes are i and (i+1) mod 3 . Therefore the common edge between faces f1 and f2 is the edge 1 of the face f1 and the edge 0 of the face f0.
vcg::face::FFAdj stores, for each edge e of the face f:
-
FFp(e): a pointer to a face f' sharing e. If e is a border than points to the face f itself -
FFi(e):the index of e in the pointed face
|
|
Note that we specified that FFp(e) point to a adjacent face, not to the adjacent face.
The reason is that there could be more than two faces on the same edge, and this is seamlessly supported by VCG Lib.
In the picture below is shown an example with 4 faces incident to the same edge
In this way VCG Lib provides a way to check if a mesh is manifold on a specific edge because the FF adjacency relation is mutual, i.e. the face f0 points to the face f1 which points to the face f0, if and only if the mesh is manifold over the corresponding edge.
bool IsManifold(MyFace *f,int e) { return (f == f->FFp(e)->FFp(f->FFi(e)))}
Referring to the picture:
(f0 == f0->FFp(0)->FFp(f0->FFi(0))} equals
(f0 == f1->FFp(0)} equals
(f0 == f0} Ok! It is manifold
Pos
The Pos is the VCG Lib implementation of the Cell-Tuple{ref}. Here we give a as short as possible definition, trading formalisms for space. A Pos in a triangle mesh is a triple made of a vertex: pos = (v,e,f) , such that v is an extreme of e and e belong to the face f . The figure shows few pos in a triangle mesh as small triangles "pointing" to a vertex, "leaning" against an edge and inside a face. For example c0=(v,e0,f).
A very nice property is that given a pos c, there is only another neighbor pos c' that can be obtained from c changing only one of the elements of the triple.
We call the operation of passing from a pos to one of its neighbors Flip and write FlipV, FlipE and FlipF to indicate that the flipped element is the vertex, the edge or the face respectively.
For example consider c1: there is only another pos which is the same as c0 except for the vertex component of the triple, and it is c2. For brevity, we write c2 = FlipV(c1).
In the left of the table some other examples are shown just to make this point clear.
|
Bounce CW around v Bounce |
Note that concatenating two flips: FlipF and FlipE we obtain a transition from a pos to the next in counterclockwise or in clockwise sense, depending if the starting pos is on the CCW edge of the face with respect to the vertex or not. Also note that, thanks to how FF adjacency is defined, when a pos is on the border, it bounces back.
This pair of flip are widely used in the VCG Lib to run over the one ring neighborhood of manifold vertices.
The following code snippet shows how to use the pos to iterate around a vertex:
/* vcglib/apps/sample/trimesh_pos_demo/trimesh_pos_demo.cpp */
#include <vcg/simplex/face/pos.h> // include the definition of pos
//...includes to define your mesh type
//class MyVertex: ...
class MyFace: public vcg::FaceSimp2<MyVertex,MyEdge,MyFace, vcg::face::VertexRef, vcg::face::FFAdj>{};
void OneRingNeighborhood( MyFace * f)
{
MyVertex * v = f->V(0);
MyFace* start = f;
vcg::face::Pos<MyFace> p(f,0,v);// constructor that takes face, edge and vertex
do
{
p.FlipF();
p.FlipE();
}while(p.f!=start);
}
Two important notes:
- We arbitrarily picked
f->V(0)as pivot vertex. In general one may want to start knowing the vertex. This is done by including the attributevcg::vertex::VFAdjwhich basically associates to each vertex pointer to one of the faces incident on it. See the VF Adjacency for details. - This implementation does not work if the vertex is on the border. Just try with the example: from the pos c4 it would find c5,c6,c3 which is in the same face as c4. Of course this does not happen if you use the pos itself as a guard and not just the face. However, even in this case, you would obtain the sequence of pos: c5,c6,c3,c1,c0,c4 corresponding to the faces f2,f2,f1,f0,f0,f1 which probably is not what you want. VCG Lib provides a variation of pos that solves this problem
Jumping Pos
The Jumping Pos works exactly like the Pos, only it does not bounce when it encounters the border. Instead, it jumps around the shared vertex of the border-faces as if they were adjacent (faces f0 and f2 in the image).
/* vcglib/apps/sample/trimesh_pos_demo/trimesh_pos_demo.cpp */
#include <vcg/simplex/face/jumping_pos.h> // include the definition of jumping pos
//...includes to define your mesh type
//class MyVertex: ...
class MyFace: public vcg::FaceSimp2<MyVertex,MyEdge,MyFace, vcg::face::VertexRef,vcg::face::FFAdj>{};
void OneRingNeighborhoodJP( MyFace * f)
{
MyVertex * v = f->V(0);
MyFace* start = f;
vcg::face::JumpingPos<MyFace> p(f,0,v);// constructor that takes face, edge and vertex
do
{
p.NextFE();
}while(p.f!=start);
}
VF Adjacency
VCG Lib implements vertex-to-face adjacency, i.e. given a vertex v we can retrieve all the faces incident to v. Let v_star =(f0,f1,f2,...,fk) be the set faces incident to v arranged in a sequence (with no preferred criteria). VCG Lib allows to retrieve v_star in optimal time ( O(#star_v) by using the following attibutes:
-
vcg::vertex::VFAdjwhich is a vertex attribute containing a pointer to f0 -
vcg::face::VFAdjwhich is a face attribute containing a pointer to the next face in the list v_star for each of its 3 vertices (4 in the case of tetrahedra)
These two attributes are not only pointers, they also contain an index referring the index of the vertex in the pointed face in the same style as the vcg::face::FFAdj does.
The picture below shows a complete example:
|
|
VFIterator
VFIterator is a simple iterator to run over the faces in the one-ring neighborhood of a vertex using the VF Adjacency (it is just like Pos for the FF Adjacency) The following code snippet shows how to use the VFIterator:
/* vcglib/apps/sample/trimesh_pos_demo/trimesh_vfiter_demo.cpp */
#include <vcg/simplex/face/pos.h> // include the definition of VFIterator
//...includes to define your mesh type
class MyVertex: public vcg::VertexSimp2<MyVertex,MyEdge,MyFace, vcg::vertex::VFAdj /*,... other attributes*/ >{};
class MyFace: public vcg::FaceSimp2<MyVertex,MyEdge,MyFace, vcg::face::VertexRef,vcg::face::VFAd>{};
void OneRingNeighborhoodVF( MyVertex * v)
{
vcg::face::VFIterator<MyFace> vfi(v); //initialize the iterator tohe first face
for(;!vfi.End();++vfi)
{
MyFace* f = vfi.F();
// ...do something with face f
}
}
Few facts on FF adjacency and VF adjacency
Here we make a series of simple statements just to avoid confusion and try to help up choosing the adjacencies the best fit your needs.
- If the mesh is manifold, the one-ring neighborhood of a vertex computed by using Pos ( needs FF adjacency) is the same as the one computed by using VFIterator (needs VF adjacency). The order in which the faces are visited can be CW or CCW if using Pos, unspecified by using VFIterator
- If the mesh is non-manifold, Pos may not find all the faces of the one-ring neighborhood of the vertex, VFIterator always does
Boundary relations and adjacency
In many algorithms you need to simply the boundary/border condition of a face, e.g. to know if a given face f has one or more adjacent faces on a specified edge e. Using FF adjacency this can be done simply by using the face::IsBorder(f,e) static function that simply checks if the pointer stored in face f on the edge e points to f itself. If you are navigating the mesh using a Pos, you have a Pos member function IsBorder() that reports the boundary condition of the current pos.
Similarly, for testing manifoldness of specific places over a mesh, there is a face::IsManifold(f,e) static function and a IsManifold(e) function member of the pos class.
If you are not using FF adjacency evaluating the boundary conditions could be not very efficient, so vcg library provides a technique to cook the current boundary conditions of the mesh into vertex and face flags. Use the members of the UpdateFlags static class to compute flags that reflects the current mesh status and the access these flags using the IsB(e) member function of the face class. Remember that flags based boundary information can become invalid if you change the mesh topology. On the other hand consider that many non-mesh-modifying algorithms do not require explicit FF adjacency but just boundary information (typical examples: most mesh smoothing and curvature computation algorithms).
Please note that the boundary flags are set true also for non manifold conditions.
Space concepts
VCG Lib implements the basic functionalities to handle geometric entities such as the point, the segment, the triangle and so on. It is very important not to confuse those geometric entities and the topological entities they can be a component of. In other words, the type vcg::Point3f (vcg/space/point3.h) is a point in 3 dimensional space and is different from a vertex (vcg::VertexSimp2<...>) which is a topological entity.
The same goes for the vcg::Segment3 and the vcg::EdgeSimp2<..>, the vcg::Triangle3 and the vcg::FaceSimp2.
Viewing and manipulation
In this section, all that concerns the definition of a view and the manipulation of a mesh will be explained.
Shot and camera
This section presents the structure of the shot and camera of the library.
After an overview of the camera model used, all the components of the shot class are listed and described.
Then, a set of examples of the most important operations (projection and un-projection) and of the interactions between shots and between a shot and the trackball are presented. Finally, simple examples of a shot are visually shown, in order to help with the implementations of eventual wrapped to and from other shot-camera formats.
The camera model
In generale, the camera parameters can be divided in two groups:
- Extrinsic (or external) parameters: these are the parameters associated to the position in the space of the camera.
- Intrinsic (or internal) parameters: these values are related to the peculiar characteristics of the camera, like the focal length (the zoom) or the distortion introduced by the lenses.
If these group of values are put in a proper camera model, it is possible to transform any point in the space in the corresponding point on the image plane of the camera (and viceversa).
In fact, given a simple perspective camera model like the one shown in figure, extrinsic parameters can be used to transform a point from its world coordinates {xw,yw,zw} to the camera 3D coordinate system {x,y,z}):
In this case the extrinsic parameters are a 3 X 3 rotation matrix R and a translation vector T, which define the orientation and position of the camera. In order to transform the 3D camera coordinate in 2D image plane coordinates (Xu,Yu) it's necessary to know the measure of the distance between the point of view and the image plane (OO1 in figure): this value, indicated with f, is usually known as the focal length. The relation between the camera and image coordinates of the point can be expressed as follows:
Another aspect of the structure of a camera that can be characterized is the distortion introduced by the lenses: if we suppose that the distortion is radial (performed along the radial direction respect to the center of distortion) we can calculate the undistorted image coordinates
where
and
In conclusion, a quite accurate model of a camera can be described by:
- A 3x3 rotation matrix and a translation vector for extrinsic parameters
- The values of focal, center of distortion and one or more distortion coefficients for intrinsic parameters
While this set of parameters provides everything to transform any 3D point in its corresponding point in the image plane, this could be not enough in peculiar applications. If an accurate estimation of the real position of the camera respect to the object is needed, some more data about the camera model are needed: in particular, the sensor physical size together with the resolution in pixel of the acquired image. If these a-priori data are known, a unique set of camera parameters is associated to any shot.
The VCG Shot
The implementation of a Shot in the VCG library can be found in vcg\math\shot.h
The shot is composed by two elements:
- the Extrinsics parameters, which are stored in the class Shot (in the type ReferenceFrame) that contains viewpoint and view direction.
The Extrinsics parameters are kept as a rotation matrix "rot" and a translation vector "tra" NOTE: the translation matrix "tra" corresponds to -viewpoint while the rotation matrix "rot" corresponds to the axis of the reference frame by row, i.e.
rot[0][0 1 2] == X axis
rot[1][0 1 2] == Y axis
rot[2][0 1 2] == Z axis
It follows that the matrix made with the upper left 3x3 equal to rot and the 4th column equal to tra
and (0,0,0,1) in the bottom row transform a point from world coordinates to the reference frame
of the shot.
- the Instrinsics parameters, which are stored as a Camera type (check vcg/math/camera) and that
determines how a point in the frame of the camera is projected in the 2D projection plane. This information was kept indendent of the extrinsic parameters because more than one shot can share the same intrinsic parameters set.
The attributes of a Camera, which is define in vcg\math\shot.h, are:
//------ camera intrinsics ScalarType FocalMm; /// Focal Distance: the distance between focal center and image plane. Expressed in mm Point2<int> ViewportPx; /// Dimension of the Image Plane (in pixels) Point2< S> PixelSizeMm; /// Dimension in mm of a single pixel Point2< S> CenterPx; /// Position of the projection of the focal center on the image plane. Expressed in pixels Point2< S> DistorCenterPx; /// Position of the radial distortion center on the image plane in pixels S k[4]; /// 1st & 2nd order radial lens distortion coefficient (only the first 2 terms are used) //------------------------
While the extrinsic parameters usually change between the shots, some (sometimes all) of the intrinsics are strongly related to the camera model used. In particular, some values are usually known before camera calibration: viewportPx and CenterPx. Moreover, if an accurate calibration is needed, it is necessary to fill the PixelSizeMm value.
This can be inferred from the camera datasheet (it can be calculated by dividing the sensor width and height, in mm, by the resolution of the image) or, in some cases, from the EXIF of the image (in the CANON models, it is the inverse of FocalPlane X-resolution and FocalPlane Y-resolution, scaled from inches to mm if necessary). If a correct PixelSizeMm is not set, the values of the camera parameters can be different from the real ones, even though the image is perfectly aligned to a 3D model.
Also the focal distance can be inferred from EXIF, but its value is indicative. If a calibrated camera is used, then all the intrinsic parameters should be known in advance.
File Formats
VCGLib provides importer and exporter for several file formats
- import: PLY, STL, OFF, OBJ, 3DS, COLLADA, PTX, V3D, PTS, APTS, XYZ, GTS, TRI, ASC, X3D, X3DV, VRML, ALN
- export: PLY, STL, OFF, OBJ, 3DS, COLLADA, VRML, DXF, GTS, U3D, IDTF, X3D
The following code snippet show how to use the PLY importer and exporter:
#include <wrap/io_trimesh/import.h> // definition of type MyMesh MyMesh m; vcg::tri::io::ImporterPLY<MyMesh>::Open(m,"namefile_to_open.ply"); // .... vcg::tri::io::ExporterPLY<MyMesh>::Save(m,"namefile_to_save.ply");
The previous one is the minimal required interface for all the importer and exporters. Additionally two other parameters can be specified: mask and callback .
The callback is used for providing a feedback during the usually lenght importing/exporting process. The mask is used to better specify/understand what is loaded/saved.
Saving Mask and Reading Mask
Generally all file formats save at least vertex positions and basic connectivty information, but beyond that you may want to choose
which other data are stored with the file. To this aim, VCGLib provides a class vcg::tri::io::Mask which is essentially a
collection of bit names that are used to specify which components you want to write to the file (e.g. stuff like vcg::tri::io::Mask::IOM_VERTCOLOR).
When saving this mask is used in a READ ONLY mode (they are just constants) to specify what component you want to save. For example, if you have stored in your mesh normals per vertex (for rendering purposes) but you consider saving them in ply format just a waste of space, you should specify an appropriate mask with the Mask::IOM_VERTNORMAL bit cleared.
Obviously not all the formats are able to save all the possible data. For example STL format cannot save texture coords or per vertex color. So to know what you are able to save in a given XXX format there is the function ExporterXXX::GetExportMaskCapability() that gives you a bitmask with all the capability of that file format.
When loading this mask is used in a WRITE ONLY mode to report to the user what fields have been loaded from the file into the mesh (e.g. the initial value of the mask is ignored). In many cases it could be useful to know what data is present into a file to prepare the mesh (e.g. to enable optional components), for this purpose all the importer have also a ImporterXXX::LoadMask(filename,mask) that fill the mask only without effectively loading a mesh. Note that for some mesh formats understanding what is contained into a file means parsing the whole file.
Error Reporting
The mesh I/O functions returns ZERO on success and an error code different from zero in case of failure or critical conditions. Use the static const char *ErrorMsg(int error) function to get a human understandable description of the error code and static bool ErrorCritical(int err) to know if a given error is a critical one or just a warning. For example in the OBJ format the absence of the accompanying material description file, is considered non critical (you are able to get the correct geometry even if you miss material info).
VMI dump file
VMI is an acronym for Vcglib Mesh Image and it is not a proper file format but a simple
dump of the vcg::TriMesh on a file.
Not being a file format means that:
- there is no specification
- it is bound to the current version of the VCGLib with no backward compatibility
a VMI can be very useful for debug purpose, because it saves:
- all the components
- all the temporary components (currently only of Optional Component Fast type)
- all the vertex, face or mesh attributes you have in your mesh file
So if, for example, your algorithm crashes at the n-th step, you can save intermediate results of your computation with VMI and reload it.
Note that in order to load a VMI the mesh passed as template to vcg::tri::ImporterVMI<MeshType>(..)
must be of the same type as the mesh passed to vcg::tri::ExporterVMI<MeshType>(..) otherwise
the loader returns FALSE.





