Tutorial

From vcg Library

Jump to: navigation, search

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 include from here to anything else than standard libraries. Note that core part is made of header files (.h files) 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.h which 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_ocf as 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_occ as the container of your vertices.
  • specify the attribute normal as optional by passing vcg::vertex::Normal3fOcc among template parameters of VertexSimp2.
/* 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_ocf or vector_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
Indexing of vertexes and edges
Indexing of vertexes and edges

f1->FFp(1) == f0
f1->FFi(1) == 0

f0->FFp(0) == f1
f0->FFi(0) == 1

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

Non manifold edge
Non manifold edge
. In this case the adjacencies are set to form a circular list (not necessarily sorted with the angle around the edge). This is done by the VCG Lib function that update these values (UpdateTopology<MeshType>::FFTopology(MeshType & m)).

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.

Image:Pos_example.png

c2 = FlipV(c1)
c0 = FlipE(c1)
c3 = FlipF(c0)

CCW around v
c4 = FlipE(FlipF(c0))
c5 = FlipE(FlipF(c4))

Bounce
c6 = FlipE(FlipF(c5))

CW around v
c3 = FlipE(FlipF(c6))
c1 = FlipE(FlipF(c3))

Bounce
c0 = FlipE(FlipF(c1))

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 attribute vcg::vertex::VFAdj which 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::VFAdj which is a vertex attribute containing a pointer to f0
  • vcg::face::VFAdj which 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:

example of vertex-face adjacency
example of vertex-face adjacency

v.VFp() == f2
v.VFi() == 0

f2->VFp(0) == f3
f2->VFi(0) == 1

f3->VFp(1) == f1
f3->VFi(1) == 2

f1->VFp(2) == f0
f1->VFi(2) == 2

f0->VFp(2) == NULL
f0->VFi(2) == -1

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).

An example scheme of a perspective camera model
Enlarge
An example scheme of a perspective camera model

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}):

Enlarge

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:

Enlarge

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

Enlarge

where

Enlarge

and

Enlarge

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.

See Also

Personal tools