OpenCASCADE 6.9.0 を Debian でビルド

先月の12日にリリースされた Open CASCADE の 6.9.0 をビルドしました。公式のロードマップでは 6.x 系列は 6.8 が最後で、次は 7.0 になるとの話だったはずですが、何だかんだで 6.9 がリリースされた模様

v6.9 のリリースノートを読んではいましたが、ここのところ忙しくて対応ができなかったので出遅れてしまいました。

ここ数バージョンは、ビルドの仕方マニュアルがいろいろと整備されてきて、時代に合わせてなのか、ビルド環境ごとの Markdown なドキュメントも付属しています。このブログで改めて紹介する必要性もあまりなくなってきました。詳しい内容は公式ドキュメントに任せるとして、要点だけかい摘まんで紹介したいと思います。

公式ドキュメンテーション

v6.9.0 の内容は次のドキュメントで網羅されています。

v6.9.0 のリファレンスマニュアルは、ソースコード tar ボールや Windows インストーラー内に同梱されています。最も最新バージョン(開発版)のリファレンスマニュアルであればこちらで参照できます。

v6.9.0 リリースの要点

モデリング・アルゴリズム

ファジー ブール演算

これまでのバージョンでは、ソリッドモデル等で行うブール演算時に対象と対象がちょうど同一の位置にある面や線を共有して存在していた場合、意図した演算結果にならない事が多くありました。意図した結果にならない場合はまだ良かったんですが、いつまで経っても計算が帰ってこない、または例外を吐いてお亡くなりになるケースもありました。この点が改善されているようです。

複数の引数を取るブール演算

これまでのブール演算では TopoDS_Shape を二つ用いて、Fuse, Common, Cut 等の演算を実行していました。あんまり詳しく見てませんが、同時に複数の TopoDS_Shape を演算させることができるようになるようです。これまで数回の処理で書いていたものが一度にできるわけですが、複数のソリッド群の一つに reverse させたソリッドを混ぜて Common に投げて、結果的にその部分だけは Cut させる動作にするなど、より「ブーリアン」っぽい演算としてコーディングできるようになりそうです。

ビジュアライゼーション

選択処理を再設計して、より良いパフォーマンスに

OCCT のビジュアライゼーションは長いことほったらかしになっていた様子で、ちょっと貧弱でした。これは、あくまでも OCCT は三次元幾何演算とトポロジックな形状の演算がメインであり、代替ライブラリーを用いて解決できるビジュアライゼーションは二の次でも大丈夫!という背景もあります。 実際、OCCT を採用している有名なオープンソース三次元 CAD ソフトの FreeCAD は、OCCT 提供のビジュアライゼーションを使用せずに別のライブラリで三次元ビューを提供しています。 ただ、ここ数バージョンでは、前述の幾何を扱うことろがだいぶ安定してきたのか、二の次だったビジュライゼーションが次々と強化されており、パフォーマンスも改善しています。まだ実験的な印象ではあるものの、レイトレーシングレンダリングの採用、GLSLシェーダが読み込めるようになっていることなど、単なる計算ライブラリから本当に「CAD」として必要な機能の強化が見てとれます。 後述する VTK の採用も注目すべき点です。

OpenGL ES 2.0 互換

ビジュアライゼーションの改良の特記すべきもう一つの点は、iOS, Android などのモバイル端末への対応です。OpenGL ES はモバイル端末向けの OpenGL のサブセットです。OCCT の幾何ライブラリの部分は、早い段階から Android にも移植されて動作していましたが、ビジュアライゼーションはそのままではビルドできませんでした。ES 互換となることにより、これらの環境へもシームレスな移植が可能となりそうです。

シェイプを表示する際の三角形メッシュ化の制御

コンテキストにシェイプを追加する際、これまでは必要に応じて自動的にシェイプの描画データである三角形メッシュを内部で生成していました。これを外側から無効にするオプションが導入されたようです。 形状の変形はなく、大量のシェイプを回転させるだけでも再三角形メッシュ化が走って表示速度が落ちていましたが、それらを動的に制御することができそうです。(※回転も行列演算している意味では変形(トランスフォーメーション)ですが、自由曲面の式まで遡って、形状の三角形メッシュを再構築する必要すらない場合も往々にあります)

その他

Intel TBB ライブラリを使わない並列処理のサポート

こちらも詳しく追っていませんが、自前で並列処理を行う環境が整いつつあるようです。

サンプルに AndroidQt, JniViewer for Android が追加

Android 環境のサンプルも充実してきました。

JniViewer

JniViewer for Android

AndroidQt

GUI は QML を使って書いてあるみたいです。本当に WPF/.NET Framework なんて使ってる場合じゃないです。

AndroidQt

その他のサンプルに、Qtデスクトップ版、MFC、C# があります。C# では WPF ベースのものと System.Windows.Forms 版があり、今のご時世でいうと半ばどうでもいいですが、Direct3D デモも追加されています。

v6.9.0 の全体的な感想

幾何演算部分の改良は喜ばしい限りで、ビジュアライゼーションの強化も頑張ってほしいところです。ようやくモバイル端末のサポートも視野に入れてきているようですが、個人的にはいっそのこと三次元ビューアは WebGL 環境へシフトしていくのもアリなんじゃないかなぁと考えています。OCCT をサーバ上で並列処理させて、クライアントの WWW ブラウザ上で表現する…素晴しいじゃないですか。最近は NW.js (Node.js + Webkit)のようなツールキットも存在しますので、ライトウェイトなビューアーからワークステーションで動かす CAD まで環境に依存しないビジュアライゼーション機能になっていけばいいな、と思っています。え?お前が Context や Viewer, View を JavaScript で再実装しろって?それが仕事になればいいんだけどなぁ…;)

v.6.9.0 のビルド

ビルド方法は次のドキュメントに詳しく書いてあります。

なお、このドキュメントはソースコード tar ボールを解凍して生成されるdoxディレクトリ以下に markdown ファイルとして収録されています。(逆に言うと、上記の HTML はそれらのファイルを doxygen にかけたものと思います。)

ビルド環境

OCCT のビルド環境もかなり充実してきており、以前のようにチューニングしながらあれやこれやする必要もほとんどなくなりました。公式サポートしているのは次の環境です。

  • Automake
  • CMake
  • CMake + ADT (Android SDK)
  • Code::Blocks (Mac OSX)
  • Xcode (Mac OSX)
  • Microsoft Visual C++ (Windows)

また、パフォーマンスチューニングレポートでは Linux 版の Clang/LLVM でも検証されているようです。

今回は一番馴染みがある Automake でいきたいと思います。

ビルド手順

作業ディレクトリに入り、tar ボールを落としてきます。ソースコードは前バージョンまではユーザー登録&ログインしないとアクセスできなかった気がしますが、今回から下記の URL で落とせるようです。なお、git での clone は登録が必須です。

cd /tmp
wget http://files.opencascade.com/OCCT/OCC_6.9.0_release/opencascade-6.9.0.tgz

解凍してディレクトリにはいります。

tar zxvf opencascade-6.9.0.tgz
cd opencascade-6.9.0/

configue をビルドして configure します。オプションについては後述します。

./build_configure
./configure -prefix=/opt/occ690 \
-with-vtk-include=/usr/include/vtk-6.1 \
-with-vtk-library=/usr/lib/i386-linux-gnu/

make して install します。

sudo make -j2 install

とりあえずこれだけです。便利。

貧弱な自宅サーバ(Debian GNU/Linux、32bit環境、Intel Atom)では、make で

real    183m57.121s
user    336m24.168s
sys     16m36.896s

となりました。

configue オプション

-prefix=

最終的にビルドしたファイルをインストールする場所です。自分は /opt/occ690 に指定しています。

-with--include=, -with--library=

ヘッダのインクルードパスとライブラリパスを追記しています。VTK が見えてなかったようなので、明示的に指定しました。

その他のオプションはこちらを参考にしてください。

ビルド時のテクニック

configure では、ビルドする環境にインストールされているライブラリの有無によって OCCT のモジュールとよばれるライブラリ群の Makefile を生成します。

モジュール > ツールキット > パッケージ > クラスや列挙型など

OCCT は上記のように階層構造を持ったライブラリです。ツールキットの部分が共有ライブラリファイル(.so)に当たります。Windows ではダイナミックリンクライブラリ(.dll)になります。モジュールはそれらをまとめて用途別にグループにしたものだと思ってください。

モジュールには次のものがあります。

  • FoundationClasses … 共通関数などを提供
  • ModelingData … 幾何形状をデータとして表現する機能
  • ModelingAlgorithms … 幾何形状を構築する機能
  • Visualization … 幾何形状を画面に描画する機能
  • ApplicationFramework … OCCT を使ったアプリのための有用な機能
  • DataExchange … 幾何データファイルのサポート(IGES,STEP,STLなど)
  • Draw … テスト環境

ざっくりと説明すると上に行くほど依存関係が少なくてすみます。詳細な依存関係はリファレンスマニュアルの図のとおりです。

occt modules

さらに、個々のモジュール内のツールキットの中で依存関係があります。なお、IGES などのデータファイルをサポートするには、GUI が必要がない場合でもビジュアライゼーションやアプリケーションフレームワークの各ツールキットを全て背負い込まなければならないかというと、実はそうではありません。具体的にはモジュール同士の依存関係の奥にはツールキット同士の依存関係があり、それを満たしていればこの図の限りではありません。今のところ、モジュールを越えた関係図はないので、リファレンスマニュアルを見て何が必要なのかを調べる必要があります。

さて、ビジュアライゼーション以下のモジュールは、さらに多くの別の人(サードパーティ)が開発しているグラフィック系のライブラリに依存しています。

そのため、グラフィック系のライブラリがない環境で configue すると、ビジュアライゼーション以下のモジュールが無効になった Makefile が生成されます。

configure の出力に各モジュール名と Yes/No といった表示が出力されるので、それでモジュールが有効になっているか無効になっているか、無効の場合はどのライブラリが足りていないのかを表示してくれます。以下に私の環境の configure 出力を示します。

3rdparty mandatory products       
---------------------------------
freetype      : yes 
tcltk         : yes 

3rdparty optional products       
---------------------------------
gl2ps         : no (--with-gl2ps=DIR option was not defined)
freeimage     : no (--with-freeimage=DIR option was not defined)
tbb includes  : no (--with-tbb-include=DIR option was not defined)
tbb libraries : no (--with-tbb-library=DIR option was not defined)
opencl        : no 
qt            : no (--with-qt=DIR option was not defined)
vtk           : yes 

Component                   Build
--------------------------  -----
FoundationClasses           yes 
ModelingData                yes 
ModelingAlgorithms          yes 
Visualization               yes 
ApplicationFramework        yes 
DataExchange                yes 
Draw                        yes 

「3rdparty mandatory products」は必須ライブラリ、「optional」は指定があったら使うライブラリです。必要なライブラリとそのバージョンは、以下にまとめてあります。

Open CASCADE Technology: Overview

Debian GNU/Linux jessie 32bit 版では、すべてのサードパーティ・ライブラリを標準リポジトリからインストールすることができました。(ソースコードの取得とビルドは不要)

また、テスト環境である Draw や Qt のサンプルプロジェクトをビルドする必要がなければ、 Tcl/Tk や Qt のインストールは必要ありません。 上記の「mondatory products」に Tcl/Tk が入っているのは、すべてのモジュールをビルドする際に必須、という意味だと思います。

siren をビルド

OCCT を使った siren というソフトを作ってます。OCCT が提供する三次元幾何演算機能を Ruby で簡単に記述することができ、ちょっとした演算なら手軽にすることができます。

上記でビルドした OCCT 6.9.0 を使って siren の最新リビジョン(OCCT6.8.0ベースで記述)をビルドし直してみると、あっけなくビルドが通りました。クラス名が変更されたり、siren で使っている機能が変更されることがなかったようです。

ひとまず安心しました。


OpenCASCADE6.7.0でレイトレーシングレンダリングを有効にする

OpenCASCADE 6.7.0 ではレイトレーシング方式のレンダリングをサポートしている事を前回紹介しました。今回は、実際にコーディングをして試してみたものを紹介します。

前提

レイトレーシングレンダリングが何ぞや、というのは、この記事を読んでいる方なら既にお分かりだと思いますので割愛します。OCCTにおけるレイトレーシングの設定は、AIS コンテキストでも Viewer でもなく、View に付随しています。レンダリング、つまり見た目の設定なので、当たり前と言えば当たり前ですね。これは逆に言うと、同じViewerにぶら下がっているいくつかの View は、それぞれ別のレンダリング方式を設定することができるという訳です。 また、レイトレーシングをサポートした事により、それまでの方式と区別するためにOCCTでは次のようにモードとして呼称しています。

  • Raytracing Mode レイトレーシング・モード
  • Rasterization Mode ラスタライズ・モード(従来からあるレンダリング方式)

レイトレーシングがサポートされたことによって、従来からあるラスタライズ・モードが使えなくなるわけではありません。バージョン6.7.0以前のコードも(見た目まわりに関しては)レイトレーシング・モードのサポートによって書き換えの必要はなく、そのままで従来方式のレンダリングでの表示が可能です。

使い方

では、実際にコードを見てみましょう。

void OCCViewer::initViewAppearance(bool is_raytracing, bool is_shadow, bool is_antialias, bool is_reflection)
{
    if (is_raytracing) { // Enable ray tracing mode
        view->SetRaytracingMode();
        view->EnableGLLight(Standard_True);
        is_shadow     ? view->EnableRaytracedShadows()      : view->DisableRaytracedShadows();
        is_antialias  ? view->EnableRaytracedAntialiasing() : view->DisableRaytracedAntialiasing();
        is_reflection ? view->EnableRaytracedReflections()  : view->DisableRaytracedReflections();
    }
    else { // OpenGL rasterize rennaring mode
        view->SetRasterizationMode();
    }
    // ...
}

これは私が開発しているナンチャッテCADビューアー「siren」のソースコードの一部です。レイトレーシングレンダリングを有効にするには、新たに用意されたメソッド呼び出すだけで済み、非常に簡単です。 解説しなくとも書いてあるとおりですが…一応ざっくりと紹介しておきます。

V3d_View::SetRaytracingMode() Viewをレイトレーシングモードに設定します。

V3d_View::SetRasterizationMode() Viewを従来(ラスタライズした形状をOpenGLに投げて描画する)方式に設定します。

V3d_View::EnableRaytracedShadows() / DisableRaytracedShadows() レイトレーシングによる影の表現(陰ではない)を有効・無効にします。

V3d_View::EnableRaytracedAntialiasing() / DisableRaytracedAntialiasing() レイトレーシングモードの時、アンチエイリアスをかけたレンダリングを有効・無効にします。

V3d_View::EnableRaytracedReflections() / DisableRaytracedReflections() レイトレーシングモードの時、形状の鏡面反射を有効・無効にします。反射率は AIS_Shape に関連付けされたマテリアル(材質)によって変化します。

これらのメソッドをViewの初期化時に呼んでやれば、レイトレーシングによるレンダリングを行います。すべてのオプションを有効にすると、次のように描写されます。  

occt670-raytracing-sample

球と面を配置して、材質設定を「金」にしただけですが、綺麗ですね!  

やっぱり重い

私の作業PC(NVIDIA NVS 3100M)ではお世辞でも快適とは言えないくらい動作が遅くなってしまいました。とは言うものの、リアルタイムでレイトレーシングレンダリングをしているわけですから、昔のPCに比べれば、対話的に視点を回転できるだけでもびっくりです。弟の作業PC(もっと良いグラボを積んでます)でレイトレーシングモードを有効にしたところ、少ない形状数であれば、ほぼリアルタイムでぐりぐり動いているようです。 思いっきりハードウェアパワーに依存しているところですので、レイトレーシングモードの濫用は避けるべきだと思います。MayaやMAX、Blenderといった3D CGアニメーションソフトも、作業画面とレイトレーシングが有効にできるレンダリング画面は別になっていますし、用途に合わせて使っていければいいと思います。 レイトレーシングモードに絡んで、新しく追加されたGLGSベースのシェーダーとライトについては、次の記事で紹介したいと思います。

OpenCASCADE のオブジェクトを WebGL で表示する

OpenCASCADE の Python 実装である PythonOCC の人が、2011年の1月のブログ記事で OpenCASCADE のオブジェクトを WebGL 向けに出力する方法を紹介しています。2年以上経った今、ウェブブラウザも WebGL に対応してきましたし、OpenGL 3.x をサポートしたオンボードチップも一般化してきました。精密な曲線・曲面表現の問題を抜きにすると、WebGL という手軽に三次元モデルが表示できる環境は魅力的だと考えています。メタセコイアの MQO 形式も WebGL で表示ができるよう作っている方もいますし、夢は広がるばかりです。

WebGL については、OpenGL ハードウェアにレンダリング作業を投げたり、HTML の img タグの data スキーム内のデータをリアルタイムに書き換えてアニメーションさせている、という程度の漠然とした知識しかありませんでした。綺麗なデモを見てみると、ちゃんと JavaScript を勉強してやってみたいなあ、花形的なジャンルだなあとは思いつつ…、言語自体は綺麗で好みなのですが、なんだかなあ。

先日、弟と呑みに行って WebGL の話が出て、PythonOCC の人が OpenCASCADE のオブジェクトを WebGL 向けに出力していることを思い出しました。

こちらのデモページのように、OpenCASCADE で作成したオブジェクトがウェブブラウザの中で、ぐりぐり動かせて三次元表示されています。ソースコードを見てみると、TopoDS_Shape オブジェクトを三角形メッシュ化して、WebGL で読み込み、表示ができるフォーマットに出力し直しているようです。

私自身は、PythonOCC を利用していないので、このコードを参考に C++ で実装し直そうと思いました。

#include <iostream>
#include <TopoDS.hxx>
#include <TopoDS_Face.hxx>
#include <BRepTools.hxx> // BRepTools::Read()
#include <BRep_Tool.hxx> // BRep_Tool::Triangulation()
#include <BRep_Builder.hxx>
#include <BRepMesh.hxx>
#include <TopExp_Explorer.hxx>
#include <Poly_Triangulation.hxx>

#define MESHPREC 1000.0

using namespace std;

int main(int argc, char** argv)
{
  // 引数チェック
  if (argc != 2) {
    cerr << "The brep2webgl convert an OpenCASCADE Brep file format\n"
      "to ECMAScript script for WebGL.\n"
      "Usage:\n"
      " brep2webgl infile.brep > outfile.js\n"
      << endl;
    return 1;
  }

  // 読み込み
  TopoDS_Shape shape;
  BRep_Builder bb;
  BRepTools::Read(shape, argv[1], bb);
  if (shape.IsNull()) {
    cerr << "Convertion incompleted." << endl;
    return 1;
  }

  cout <<
  "var Shape = function() {"
    "var scope = this;"
    "THREE.Geometry.call(this);";

    cout << endl;

    BRepMesh::Mesh(shape, MESHPREC);
    TopExp_Explorer ex(shape, TopAbs_FACE);
    double scale = 0.001;

    for (int vindex = 0; ex.More(); ex.Next()) {
      TopoDS_Face face = TopoDS::Face(ex.Current());
      TopLoc_Location loc;
      Handle(Poly_Triangulation) poly = BRep_Tool::Triangulation(face, loc); // 三角形パッチ化
      const Poly_Array1OfTriangle& tris = poly->Triangles();
      for (Standard_Integer i = tris.Lower(); i <= tris.Upper(); i++) {
        const Poly_Triangle& tri = tris.Value(i);
        Standard_Integer n1, n2, n3; // ノードインデックス
        if (face.Orientation() == TopAbs_REVERSED)
          tri.Get(n3, n2, n1);
        else
          tri.Get(n1, n2, n3);
        gp_Pnt p1 = poly->Nodes().Value(n1);
        gp_Pnt p2 = poly->Nodes().Value(n2);
        gp_Pnt p3 = poly->Nodes().Value(n3);

        cout <<
          "v(" << p1.X() * scale << "," << p1.Y() * scale << "," << p1.Z() * scale << ");"
          "v(" << p2.X() * scale << "," << p2.Y() * scale << "," << p2.Z() * scale << ");"
          "v(" << p3.X() * scale << "," << p3.Y() * scale << "," << p3.Z() * scale << ");"
          "f3(" << vindex + 0 << "," << vindex + 1 << "," << vindex + 2 << ","
          << p1.X() << "," << p1.Y() << "," << p1.Z() << ","
          << p2.X() << "," << p2.Y() << "," << p2.Z() << ","
          << p3.X() << "," << p3.Y() << "," << p3.Z() << ");";
        cout << endl;
        vindex += 3;
      }

    }

    cout
      << endl << "this.sortFacesByMaterial();"
      << endl << "function v(x,y,z){scope.vertices.push(new THREE.Vertex(new THREE.Vector3(x,y,z)));}"
      << endl << "function f3(a,b,c,x,y,z,x2,y2,z2,x3,y3,z3){"
      << endl << "scope.faces.push(new THREE.Face3(a,b,c,"
      << endl << "[new THREE.Vector3(x,y,z),new THREE.Vector3(x2,y2,z2),new THREE.Vector3(x3,y3,z3)]));}"
      << endl << "}"
      << endl << "Shape.prototype=new THREE.Geometry();"
      << endl << "Shape.prototype.constructor=Shape;";

  return 0;

}

ビルドは次のとおり。BRepToolとMeshを使っているので、それぞれライブラリを指定してやります。

$ g++ -g -lTKernel -lTKMath -lTKBRep -lTKMesh -L/usr/lib/opencas -I/usr/include/opencascade -o brep2webgl brep2webgl.cx

このプログラムは、引数に与えられた BRep ファイルを読み込んで、内容物を三角形メッシュ化して前述の環境 pythonocc_webgl.html および Three.js で読み込める JavaScript 形式のデータを標準出力に吐きます。 実際に実行するには、次のようにタイプします。

$ brep2webgl test.brep > shape.js

標準出力にスクリプトが印字されますので、shape.js にリダイレクトをして保存し、pythonocc_webgl.html で読み込めば三次元モデルデータが表示されます。

試しに制作したデモをこちらに置いておきます。結構大きめのIGESファイルをBRepに変換後、上記のプログラムを介してWebGLで表示できるようにしたものですので、読み込みに時間がかかるかもしれません。 ※平面の法線ベクトルの関係で、デモを開いた状態では、ほとんど何も表示されていないかもしれません。マウスの左ボタンで画面を軽くドラッグしてやると、物体が回転してものが表示されるようになるはずです。