OpenMesh
|
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:
Choose between triangle mesh and general polygonal mesh.
Select the mesh kernel
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
.
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.
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.
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
.
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:
MyMesh::Point
and MyMesh::Scalar
. MyMesh::Vertex
, MyMesh::Halfedge
, MyMesh::Edge
, MyMesh::Face
. 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
MyMesh::Point
and the resulting scalar type MyMesh::Scalar
== MyMesh::Point::value_type
, MyMesh::Normal
MyMesh::Color
All these customizations are encapsulated in one class MyTraits
, that is used as template argument to the mesh, e.g.
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:
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 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
.
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.
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:
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:
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:
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.
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
The macro VertexTraits
hides some ugly template stuff. In fact, it is defined as
hence the traits class actually looks like this:
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:
Adding elements to other mesh items works in the same manner.
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.
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.
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.
That's it.