大丈夫です。間違ってません。これはWebGL Advent Calendar 2015の二日目の記事です。
さて、.obj、.ply、.wrlなど、3Dモデルのためのファイルフォーマットはすでに数多くありますが、本年4月、新たに3MF(3D Manufacturing Format)というファイルフォーマットが公開されました。このファイルフォーマットの制定に中心的な役割を果たしているのはマイクロソフト社です。しかし、なぜマイクロソフト社が3Dモデルファイルフォーマットを制定しようとするのでしょうか?
実は3MFはその正式名称(?)、3D Manufacturing Formatからも分かる通り、3Dプリンタで使用することを目的としたファイルフォーマットです。つまりマイクロソフト社は紙のプリンタと同じくらい当たり前に3DプリンタがPCに繋がる未来を見据えてこのようなファイルフォーマットを推進しているのです。
CGで使うための3Dモデルと、製造機器の入力として与えるための3Dモデル、同じ3Dモデルと言っても目的が違えば制約が異なります。CGで使用する場合は見た目が全てですが、製造機器に与える場合はその形状が物理的に存在できるものでなければいけません。具体的には以下の様なものがあると製造できません。
- 厚みのないポリゴン
- ポリゴンの交差
- 内向きの法線
加えて、一般的な家庭用3Dプリンタで出力する場合はせり出し部を支えるためのサポート材や内部を充填するための構造も必要になります。
現在の3Dプリンタ用ファイルフォーマットのデファクトスタンダードはSTLですが、このファイルフォーマットは3Dプリンタが登場した最初期からあるもので、基本的には3つの頂点座標と法線ベクトルの組み合わせ(facet)を必要な数だけ並べて形状を指定するだけという単純で非常に冗長なものです。また3Dプリンタ用のデファクトファイルフォーマットと言っても、先に書いたような3Dプリントに特徴的な情報が含まれているわけではありません。
一方、3MFはzip圧縮されたXMLで、インデックス化された座標を使用することができるため(STLと比べれば)ファイルサイズが小さくなり、さらに以下の様な3Dプリンタ向けの機能も含みます。
- 特定のプリンタ、モデリングツールなどで利用可能な拡張機能の定義と取り込み
- プリント素材に関する情報
- サポート材や内部の充填構造に関する情報
3MFの普及を推進する3MF Consortiumにはすでに3D Systems、HP、Stratasysなど主だった3Dプリンタメーカーが参加しているだけでなく、Autodesk、Materialiseなどのソフトウェア会社も参加しています。そしてWindows10には3MFを表示したり書き出すことのできるアプリが初めからプリインストールされています。まさに業界を挙げて推進しているといえる状態で、将来的に3Dプリント業界の標準ファイルフォーマットになる可能性はかなり高いのではないでしょうか。
と、まぁこの辺までが前振りなんですが、そろそろなぜこのエントリがWebGL Advent Calendarとして公開されているのか疑問に思っている頃かもしれません。前ふりが長過ぎたので結論はさっさと書きます。要するに
この3MFをthree.jsで読み込むためのTHREE.ThreeMFLoaderを書いてpullreqして無事取り込まれました。ドヤァ!
というお話です。
実装に関しては特に大したことはしてなくて仕様通り愚直にXMLをパースしてるだけです。一部抜き出すとこんな感じ。愚直。
function parseObjectNode( objectNode ) {
var objectData = {
type: objectNode.getAttribute( 'type' )
};
var id = objectNode.getAttribute( 'id' );
if ( id ) {
objectData[ 'id' ] = id;
}
var pid = objectNode.getAttribute( 'pid' );
if ( pid ) {
objectData[ 'pid' ] = pid;
}
// ...snip...
return objectData;
}
Loaderを作りたい人のために1つ書いておくと、後々頂点を変更することのない静的な形状の場合はTHREE.BufferGeometry
を使うというのが昨今の傾向のようです。少し面倒くさくなりますが手を抜かずに使っておけばいいと思います。
function buildMesh( meshData, data3mf ) {
var geometry = new THREE.BufferGeometry();
geometry.setIndex( new THREE.BufferAttribute( meshData[ 'triangles' ], 1 ) );
geometry.addAttribute( 'position', new THREE.BufferAttribute( meshData[ 'vertices' ], 3 ) );
if ( meshData[ 'colors' ] ) {
geometry.addAttribute( 'color', new THREE.BufferAttribute( meshData[ 'colors' ], 3 ) );
}
geometry.computeBoundingSphere();
// ...snip...
}
なお、pullreq作りで一番つらかったのは機能の実装ではなくてソースコードをmrdoobifyすることでした。見れば分かるけど結構クセのあるスタイル。おかげでむっちゃ縦長なコードになった。
なぜこんなスタイルなんだ、Mr.doobよ・・・。
ということでこちらからは以上なんですが、最後に1つ強調しておきたいことがあります。どうやら
three.jsのexamples以下は取り込まれる基準がむっちゃ低そうです。
今回のpullreqも一瞬で取り込まれてびっくりしました。著名なライブラリの末席に自分の名前を刻んでみたい人にはオススメなので、ぜひ皆さんもなんかよさ気なexamplesを作ってpullreqしてみればいいんじゃないでしょうか。既存のコードを参考にしやすいので特にLoaderがオススメです。