HTML5+WebGLシーンがブラウザで簡単に作れる「Goo」を試してみた

siren で船型を作成し、Maya で艤装を作成して、Blender に持っていって Unity 向けに出力した FBX 形式の駆逐艦「島風」のモデルを試しに読み込ませてみると、難なく使うことができました。

上の画面内でマウスをドラッグすることにより、視点を変更することができます。また、Android の Firefox や Chrome でも、こちらの URL に直にアクセスすることにより、3D 表示&フリックによる視点回転とズームができました。

簡単な操船や海戦ゲームくらい作れそうですね!

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で表示できるようにしたものですので、読み込みに時間がかかるかもしれません。 ※平面の法線ベクトルの関係で、デモを開いた状態では、ほとんど何も表示されていないかもしれません。マウスの左ボタンで画面を軽くドラッグしてやると、物体が回転してものが表示されるようになるはずです。