OpenMesh: Specifying your MyMesh

| OpenMesh

Specifying your MyMesh

This section will show how to build your own custom tailored type `MyMesh`.

As we have seen in the section on goals and features there are some parameters to be specified for a mesh. This is done in the following four steps:

1. Choose between triangle mesh and general polygonal mesh.

2. Select the mesh kernel

3. Parameterize the mesh by a so-called Traits class. You can add arbitrary classes to the mesh items, specify the types `Scalar`, `Point`, `Normal` and `Color`, and use predefined attributes like `Attributes::Normal` and `Attributes::Color`.

4. Dynamically bind data to the mesh or the mesh entities (vertex, (half-)edge, face) using custom properties.

We will explain these four parameterization steps and give a code example at the end of this page.

# Polygonal or Triangle Mesh?

This decision is quite simple: Whenever possible choose the triangle mesh. The restriction to triangular faces usually leads to more efficient algorithms (e.g. rendering triangles is much faster than rendering arbitrary polygons). Additionally some algorithms are only implemented for triangle meshes while triangle meshes inherit the full functionality of polygonal meshes. For a list of them refer to the following links.

See also
  OpenMesh::PolyMeshT
  OpenMesh::TriMeshT

# Choosing the right kernel

The mesh kernel specifies how the mesh entities (vertices, (half-)edges, faces) are internally stored. In fact the entities are kept in so-called properties. A property itself provides an array like interface. The kernel defines the corresponding handle types, i.e. the way items reference each other. Since the properties have an array like interface the handles are represented internally as indices.

The default kernel is `ArrayKernelT`. Which is good for most situations. But depending on the application a different kernel would be better. E.g. the OpenSG integration has been realized be replacing the kernel by a custom kernel, since OpenSG provides already array like properties, which could be reused for the intergration. In case of a an OpenSG environment one might be better off using `OSG_Kernel::ArrayKernelT`.

See also
  Mesh Kernels

# Mesh Traits

While the last two sections only have chosen from a list of predefined meshes or kernels, respectively, we now come to the user-defined customization.

The resulting mesh `MyMesh` will provide the following types:

- The point and scalar type: `MyMesh::Point` and `MyMesh::Scalar`.
- The mesh items: `MyMesh::Vertex`, `MyMesh::Halfedge`, `MyMesh::Edge`, `MyMesh::Face`.
- The handle types: `MyMesh::VertexHandle`, `MyMesh::HalfedgeHandle`, `MyMesh::EdgeHandle`, `MyMesh::FaceHandle`.

While the handle types are fixed, the other types can be customized. Each mesh type (see Predefined Mesh Types) can be parameterized by a so-called traits class. Using this mechanism one can

1. change the coordinate type `MyMesh::Point` and the resulting scalar type `MyMesh::Scalar` == `MyMesh::Point::value_type`,
2. change the normal type `MyMesh::Normal`
3. change the color type `MyMesh::Color`
4. use predefined attributes like normal vector, color, texture coordinates, ... for the mesh items.
5. add arbitrary classes to the mesh items.

All these customizations are encapsulated in one class `MyTraits`, that is used as template argument to the mesh, e.g.

struct MyTraits {
// your customization
};
typedef PolyMesh_ArrayKernelT<MyTraits> MyMesh;

The rest of this section explains the construction of this traits class, its application to the mesh will be the topic of the next section.

For each mesh entity one can control the predefined attributes to be attached by a traits class using some convenience macros, e.g. `OpenMesh::VertexAttributes` and `OpenMesh::VertexTraits` for vertices. The default traits class looks like this:

struct DefaultTraits
{
typedef Vec3f Point;
typedef Vec3f Normal;
typedef Vec2f TexCoord;
typedef Vec3uc Color;
VertexTraits {};
HalfedgeTraits {};
EdgeTraits {};
FaceTraits {};
VertexAttributes(0);
HalfedgeAttributes(Attributes::PrevHalfedge);
EdgeAttributes(0);
FaceAttributes(0);
};

Please note that for example `VertexTraits` is a define concealing a template declaration. The actual template class name is `VertexT`, which is further simplified to a specific type `Vertex` at a later stage during the construction of the mesh kernel.

Because the traits classes always have to provide the template classes `VertexT`, `HalfedgeT`, `EdgeT`, `FaceT`, and the types `Point`, `Normal`, `Color`, and `TexCoord` one should derive this class from the default implementation `DefaultTraits`. In this case you will only have to define the classes or types you want to override or substitute.

## Changing the Point type

Changing the type that is used to store the point coordinates as well as the normal vectors can simply be done by defining this type in the traits class. The following code changes the coordinate type in order to use `double` instead of `float`.

struct MyTraits : public OpenMesh::DefaultTraits
{
typedef OpenMesh::Vec3d Point; // use double-values points
};

Using the OpenMesh::VectorT class you can easily plug in any scalar type for the use in point coordinates, e.g. some exact arithmetic. You can also exchange the whole class representing points as long as it provides the same interface as the OpenMesh::VectorT class.

## Adding Predefined Attributes

There are some pre-defined attributes that can be appended to the mesh items. These global attributes are defined in the namespace OpenMesh::Attributes. The advantage of these attributes is that they are registered at the items they are added to. Therefore algorithms can check for these attributes at run-time as well as at compile-time. This is important if you want to implement algorithms acting on different meshes, that may or may not have e.g. normal vectors per vertex/face.

Adding these predefined attributes is quite simple. You provide an `unsigned int` in the traits class, whose bits control whether or not a certain attribute should be attached or not.

If you want to add a normal vector to your vertices and faces, and also want to have color information for vertices, the code would look like this:

struct MyTraits : public OpenMesh::DefaultTraits
{
VertexAttributes( OpenMesh::Attributes::Normal |
OpenMesh::Attributes::Color );
FaceAttributes( OpenMesh::Attributes::Normal );
};

Internally each mesh item contains an `enum` defining the integer `Attributes` (containing the bits of used attributes OR'ed together). From its set/unset bits you can see whether a certain attribute is used. OpenMesh provides the macro OM_Check_Attrib for doing this:

if (OM_Check_Attrib(MyMesh::Vertex, Normal)
do_something_with_normals();

These run-time checks may not be sufficient in some cases. You can also check for attributes at compile-time and instantiate the correct functions by using function overloading. The class `GenProg::Bool2Type` maps true/false information to two different types, `Bool2Type<true>` and `Bool2Type<false>`. An example that draws OpenGL normals if they are available would look like this:

#include <OpenMesh/Core/Utils/GenProg.hh>
// draw a face normal if we have one
void drawFaceNormal(const MyMesh::Face& _f) {
drawFaceNormal(_f, GenProg::Bool2Type<OM_Check_Attrib(MyMesh::Face, Normal)>());
}
// normal exists -> use it
void drawFaceNormal(const MyMesh::Face& _f, GenProg::Bool2Type<true>) {
glNormal3fv(_f.normal());
}
// empty dummy (no normals)
void drawFaceNormal(const MyMesh::Face& _f, GenProg::Bool2Type<false>){}

Especially the compile-time checking for attributes is extremely useful because it does not generate any unnecessary code and does not perform expensive tests at run-time.

See also
  OpenMesh::DefaultTraits
  OpenMesh::Attributes
  OpenMesh::GenProg

## Adding User-Defined Elements

You can also add arbitrary types/elements/methods to the mesh items by providing a corresponding traits class for these items. Adding some index to the `Vertex` class is easily done by

struct MyTraits : public OpenMesh::DefaultTraits
{
VertexTraits
{
int some_additional_index;
};
};

The macro `VertexTraits` hides some ugly template stuff. In fact, it is defined as

#define VertexTraits template <class Base, class Refs> struct VertexT : public Base

hence the traits class actually looks like this:

struct MyTraits : public OpenMesh::DefaultTraits
{
template <class Base, class Refs> struct VertexT : public Base
{
int some_additional_index;
};
};

You have to keep this in mind when you want to define constructors for your vertex type or when you want to derive the vertex type from other classes.

The template argument `Base` provides access to the mesh handles and to the `Point` and `Scalar` type by its member class `Refs`. Adding a `MyMesh::FaceHandle` to the vertex class can therefore be implemented like this:

struct MyTraits : public OpenMesh::DefaultTraits
{
VertexTraits
{
int some_additional_index;
typename Base::Refs::FaceHandle my_face_handle;
};
};

Adding elements to other mesh items works in the same manner.

## Using traits defined by algorithms

From version 0.10.3 on algorithms can define traits/attributes they require and the user can merge these traits into his own traits. A more elegant way is to use dynamic properites, which can be added/removed during runtime by the algorithm. This is the preferred way to attach custom data to the mesh.

An example for an algorithm as well as the application using traits is given in Using mesh attributes and traits.

# Dynamic Properties

From version 1.0 on OpenMesh provides dynamic properties. Instead of using traits to bind data at compile time algorithms or the application can use dynamic properties. Similar to entities the properties are accessed and manipulated via handles.

An example for an algorithm as well as the application using properties is given in Using (custom) properties and Using STL algorithms.

# Final Implementation Example

Consider an application where we just want to render triangle meshes. This means we will select the triangle mesh and the `ArrayKernelT`. Faces that are not triangles will automatically be tesselated into triangles. Because we only display meshes and do not dynamically add or remove items, we can just use the `ArrayKernelT`.

All mesh-kernel combinations are predefined in the directory `OpenMesh/Mesh/Types`. Refer to Predefined Mesh Types for a complete list of them. For our example we use the `TriMesh_ArrayKernelT` and parameterize it by our `MyTraits` class.

We will need face and vertex normals and e.g. for color coding vertex curvature, i.e. vertex color.

#include <OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh>
// define traits
struct MyTraits : public OpenMesh::DefaultTraits
{
// use double valued coordinates
typedef OpenMesh::Vec3d Point;
// use vertex normals and vertex colors
VertexAttributes( OpenMesh::DefaultAttributer::Normal |
OpenMesh::DefaultAttributer::Color );
// store the previous halfedge
HalfedgeAttributes( OpenMesh::DefaultAttributer::PrevHalfedge );
// use face normals
FaceAttributes( OpenMesh::DefaultAttributer::Normal );
// store a face handle for each vertex
VertexTraits
{
typename Base::Refs::FaceHandle my_face_handle;
};
};
// Select mesh type (TriMesh) and kernel (ArrayKernel)
// and define my personal mesh type (MyMesh)
typedef OpenMesh::TriMesh_ArrayKernelT<MyTraits> MyMesh;
int main(int argc, char **argv)
{
MyMesh mesh;
// -------------------- Add dynamic data
// for each vertex an extra double value
OpenMesh::VPropHandleT< double > vprop_double;
mesh.add_property( vprop_double );
// for the mesh an extra string
OpenMesh::MPropHandleT< string > mprop_string;
mesh.add_property( mprop_string );
// -------------------- do something
...;
}

That's it.