Edited at

OpenMeshの使い方

More than 1 year has passed since last update.


OpenMeshの使い方


インストール方法

OpenMeshを使っているのでここから最新バージョンをダウンロードしてインストールしたときのメモです。

OSはUbuntu 16.04LTSです。


ダウンロードからインストールまで

cd ~/Downloads

wget https://www.openmesh.org/media/Releases/6.3/OpenMesh-6.3.tar.gz
tar zxf OpenMesh-6.3.tar.gz
cd OpenMesh-6.3
mkdir build
cd build
cmake .. && sudo make all install

管理者権限がなくローカルディレクトリにインストールする場合は

cmake -DCMAKE_INSTALL_PREFIX:PATH=$HOME/local/dir .. && make all install

となります。

共有・静的ライブラリのみ使用する場合、-DBUILD_APPS:BOOL=OFFを追加するとアプリケーションをビルドしなくなるので早く終わります。


補足

もしビルド後に○○が見つからないというエラーメッセージが出ていたらインストールしたうえで再度ビルドしましょう。私の場合は下記が見つからないと出たので関連ライブラリ(括弧内)をインストールしました。


  • glut (freeglut3, freeglut3-dev)

  • latex compiler (texlive)

  • doxygen

  • boost python (libboost-dev, libboost-python-dev, libboost-system-dev)


自作プログラムのコンパイル方法

自作プログラムでOpenMeshを使用した場合、コンパイル時にインクルードファイルと共有ライブラリへのパスを指定してあげる必要があります。前節にしたがってOpenMeshを~/.localにインストールしたとすると、次のようなオプション指定が必要となります。


  • インクルードファイルへのパス:-isystem $HOME/.local/include

  • 共有ライブラリへのパス:-Xlinker -rpath -Xlinker $HOME/.local/lib

  • 共有ライブラリ:-lOpenMeshCoreおよび-lOpenMeshTools


使い方

OpenMesh Documentationを参照しながら手探りで使い方を学びました。このDocumentationはかなりわかりにくく、検索してクラス定義を追おうとしてもたどり着かないことが多いです。

とにかく小さなコードを書いてコンパイルし、エラーメッセージから実際に何が行われているか確認するのが一番です。


メッシュデータ構造

OpenMeshではhalfedge(半辺?)データ構造を使ってメッシュを定義しているのが特徴です。

OpenMesh Documentaion: The Halfedge Data Structure

halfedgeデータ構造とは、一つの辺を異なる向きの2つの有向辺(directed edges)からなると考え、メッシュ構造をこれらの有向辺の集合として表すデータ構造のことです。


メッシュデータの基本構成要素

メッシュの基本構成要素である頂点(Vertex)・半辺(Halfedge)・辺(Edge)・面(Face)はOpenMesh/Core/Mesh/ArrayItems.hhで定義されています。これらの基本要素を指定する操作は、インデックスを使うのではなく、Handleクラス(後述)を使って抽象化されています。

各構成要素を表すクラスは次のようなデータを持っています。


  • Vertexクラスはその頂点から出て行く半辺のHandleをデータとして持っています。

  • FaceクラスはそのFaceを構成する半辺のうちの一つのHandleをデータとして持っています。

  • HalfEdgeクラスは次の要素のHandleをデータとして持っています。


    • その半辺が指している頂点

    • その半辺が属する面

    • その半辺が属する面の次の半辺(半時計回りに定義されている)

    • その半辺と同じ辺を構成する逆向きの半辺



  • Edgeクラスはその辺を構成する2つのHalfedgeをデータとして持っています。

ただしユーザーはこれらクラスが持つデータには直接アクセスできず(privateデータとして定義され隠蔽されている)、必ずメッシュのメンバー関数へ次節で説明するHandleクラスオブジェクトを渡して必要なデータを得ることになります。これらのメッシュ要素へアクセスする関数はOpenMesh/Core/Mesh/ArrayKernel.hhで定義されています。


メッシュデータへのアクセス方法


Handles

メッシュを通して頂点(Vertex)・半辺(Halfedge)・辺(Edge)・面(Face)にアクセスするために必要なクラスです。それぞれVertexHandleHalfedgeHandleEdgeHandleFaceHandle、と名付けられており、これをメッシュに渡すことで欲しい頂点・半辺・辺・面のオブジェクトへの参照を得ることができます。例えば

// メッシュの別名定義

using MyMesh = OpenMesh::PolyMesh_ArrayKernelT<>;
// メッシュ作成
MyMesh mesh;
// 頂点を扱うハンドルクラスのオブジェクトを作成
MyMesh::VertexHandle vhandle;
// 頂点をメッシュへ登録し、そのハンドルを得る
vhandle = mesh.add_vertex(MyMesh::Point(0, 0, 0));
// メッシュに登録された頂点へハンドルを使ってアクセスする
const MyMesh::Point& pt = mesh.point(vhandle);

各HandleクラスはOpenMesh/Core/Mesh/Handles.hhで定義されているBaseHandleを継承して実装されており、次のメンバー関数が利用可能です。



  • idx(): インデックスを返す


  • is_valid(): インデックスが有効かどうかを判定する


Iterators

OpenMeshでは、メッシュの基本構成要素である頂点(Vertex)・半辺(Halfedge)・辺(Edge)・面(Face)をループするためのイテレータ、VertexIterHalfedgeIterEdgeIterFaceIterが定義されており、メッシュオブジェクトを通して得ることができます。(これらのイテレータはOpenMesh/Core/Mesh/Iterators.hhを継承して実装されています。)

基本的な使い方はOpenMesh Documentation: Mesh Iterators and Circulators: Iteratorsで説明されています。例えば次のように使うことができます。

// メッシュの別名定義

using MyMesh = OpenMesh::PolyMesh_ArrayKernelT<>;
// メッシュ作成
MyMesh mesh;

/* ... ここでメッシュデータを読み込んだものとする ... */

for (auto itr = mesh.vertices_begin(); itr != mesh.vertices_end(); ++itr) {
// この場合itrはMyMesh::VertexIter型となる
// *演算子を使ってハンドルへの参照が得られる
const MyMesh::VertexHandle& vhandle = *itr;
/* ... 何らかの処理 ... */
}

となります。半辺・辺・面に対してはhalfedges_begin()halfedges_end()edges_begin()edges_end()faces_begin()faces_end()が利用可能です。

これらのイテレータを得る関数はOpenMesh/Core/Mesh/PolyConnectivity.hhで定義されています。

Range-based for loop用の関数も用意されており、次のような使い方も可能です。

for (auto&& vhandle : mesh.vertices()) {

// vhandleはMyMesh::VertexHandleの右辺値参照
const MyMesh::Point& pt = mesh.point(vhandle);
/* ... 何らかの処理 ...*/
}


Circulator

Circulatorとは、あるメッシュ構成要素に関連する要素をループする時に使うイテレータです。例えば、



  • VertexVertexIter: ある頂点に(辺を通して)接続されている全ての頂点についてループするイテレータ


  • FaceVertexIter: ある面を構成する全てのの頂点についてループするイテレータ

などです。定義されているCirculatorについては、OpenMesh Documentation: Mesh Iterators and Circulators: Circulatorsを参照してください。

例えば全ての頂点について、その頂点に接続されている頂点の情報を使った処理をしたい場合、次のようにループすることが可能です。

// メッシュの別名定義

using MyMesh = OpenMesh::PolyMesh_ArrayKernelT<>;
// メッシュ作成
MyMesh mesh;

/* ... ここでメッシュデータを読み込んだものとする ... */

for (auto itr = mesh.vertices_begin(); itr != mesh.vertices_end(); ++itr) {
for (auto ctr = mesh.vv_iter(*itr); ctr.is_valid(); ++ctr) {
/* ... 何らかの処理 ... */
}
}

CirculatorについてもRange-based for loop用の関数が定義されており、上のループを次のように書き換えることが可能です。

for (auto&& vhandle : mesh.vertices()) {

for (auto&& cvhandle : mesh.vv_range(vhandle)) {
/* ... 何らかの処理 ... */
}
}