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で表示できるようにしたものですので、読み込みに時間がかかるかもしれません。 ※平面の法線ベクトルの関係で、デモを開いた状態では、ほとんど何も表示されていないかもしれません。マウスの左ボタンで画面を軽くドラッグしてやると、物体が回転してものが表示されるようになるはずです。
こんにちは。以前のBlogでコメントを付けさせていただいた者です。
いまだにOpenCASCADEの入り口をさまよっております。笑
今日はVirtualbox下のCentOS7.0、oce-0.18環境にて、こちらのページのdyamaさまのコードを勉強させていただきました。
#include を追加し、
42行目あたりの BRepMesh::Mesh(shape, MESHPREC); を、
BRepMesh_IncrementalMesh(shape, MESHPREC); に変更することで、バイナリが出来、
JavaScript コートを得ることができました。
。。。ところで、pythonocc_webgl.html について、もう一度こちらのページなどに上げていただくことは出来ませんでしょうか。ご検討のほど、よろしくお願いします。
Tofslaさん
リプライが遅くなりました!すみません。
記事は2013年当時のものですので、OCCT6.4とかそのあたりのバージョンで(現行は6.9.1~7.0)API名も変わってきているかもしれませんね。
去年よりOCCTとRubyを使ったソフト、sirenを作成していて、そちらのビューアーで本記事と同じことをしています。
読み替えると参考になるかと思います。
・動作デモ
http://siren.xyz/siren.xyz/demo/siren-viewer.html
・ビューアーのコード
https://github.com/dyama/mruby-siren/tree/master/examples/view
→ brep2js.rb で OCCT の TopoDS_Edge と TopoDS_Face を three.js で扱える三角形面定義とポリライン定義にコンバートして model.js を生成しています。
siren-viewer.html を開くと、siren-viewer.js が呼び出され、siren-viewer.js から model.js を読みに行くようなコードになっています。
brep2js.rb が実際に三角形面を生成しているところは、
https://github.com/dyama/mruby-siren/blob/master/examples/view/brep2js.rb#L13
の face.triangle() メソッドで、このメソッドの C++ コードは、
https://github.com/dyama/mruby-siren/blob/master/src/face.cpp#L81
にあります。
OCCTの日本語フォーラムを設置していますので、よければどうぞ!
お忙しい中、ありがとうございます。
私は今、3D CADデータをWebGLで表示することに興味を持っており、dyama様のサイトにて勉強させていただいてます。
(超スローペースですが)
WEBベースでCADデータを扱えると面白いなと思っています。
rubyは勉強したことが無いので、sirenの存在は知りつつも手が伸びていなかったのですが、ひょっとしたらsirenを使うことで、OpenCASCADEでいうところのVisualizationを、WebGL上で補うことが出来るのでしょうか。
フォーラムにもおじゃましてみます。
Tofslaさん
ありがとうございます!
図面を取り扱う製造業やシミュレーション、研究分野といった活用方法があるにも関わらず、Open CASCADE の国内ユーザはなかなか見当たらず、こうやってコメントをいただけるのは励みになります。
結論から言えばそういうことになります。
私も仕事で Open CASCADE の Visualization を利用したソフトを開発して何年も経ちますが、複雑で規模も大きく、さらにリファレンスどおり動かない不具合も多く…生産性の低さに苦しめられています。
WebGL を含む JavaScript 環境はここ数年目覚しい発展を遂げており、各ライブラリのコミュニティが活発であること、裾野が広いこと、情報が多いこと…などなど OCCT の Visualization とはどれを比較しても魅力的な世界です。
siren では、「仕事ではおいそれと仕様変更できない」という足枷を外してみて、「ゆるく・楽しく・簡単に」を目指して開発を続けています。C++ で TopExp_Explorer をガリガリ回してコーディングされたことがあるのでしたら、簡単さが分かるかも…!と思います。
まだまだ足りない部分が多いですが、siren も使っていただけると幸いです。siren メインでなく OCCT を勉強する上でもお役に立てるかと思います。
私自身、仕事で OCCT の API の挙動をテストする際などで siren を用いています。:)
OCCT/siren 両フォーラムは立ち上げたばかりですので、まだほとんどトピックがないですが、お気軽にご投稿ください。私が分かる範囲であればリプライいたします!