カブクのエンジニアはこんなことしてます
こんにちは、カブクの唐揚げ担当の和田です。
「カブクのエンジニアが普段何をしているのか」ということを知ってもらうために始めたエンジニアブログですが、各エンジニアがもちまわりで書くということになり、今週もカブクのビジネスとは関係ない最近作ったものの話をいたします。
(前回CTO足立が『ハードルを地中に埋めた』といいながら、なかなかディーブなネタをキメていたので、僕も負けず劣らずディープなネタをぶっこみます)
作ったもの
Blender内の3Dデータをブロック群に変換し、それをMinecraftに転送するというBlenderのアドオンを作りました。こちらのアドオンはgithubで公開しています。
そもそも
今回は、BlenderとMinecraftを組み合わせるわけですが、これらについてちょいとご説明いたします。
Blenderって
Blenderはオープンソースの無料で誰でも使える3DCGソフトウェアです。ここ何十年、3DCGや3Dプリンタがもてはやされておりますが、その中心にある要素の一つが「3Dデータの作成」であり、このようなツールを使って作成します。
Minecraftって
Minecraftは、スウェーデンのMojang ABという会社(現在はMicrosoftが買収)がつくったゲームで、仮想空間上にブロックやらパーツを積み上げて、建物やオブジェクトを作っていくゲームです。最近は お子様にも大変人気があり、いろいろな攻略本とかも販売されているみたいです。小さいころにボンバーマンの戦略本を読みふけっていた頃が思い出されます。
プログラムの構成
基本的な流れとしては、↓のようなかんじです。下図の2つの部分について詳細に見ていきましょう。
ブロック化処理
ブロック化は、Octreeをつかって作成した各ブロックに元の3Dデータの色を彩色するという流れです。
前処理
ここではポリゴンの削減や、後々に使うデータの事前作成を行います。
- ポリゴンの削減: メッシュ交錯判定時の計算量を減らすため、元データのポリゴンを削減します
- kd木の作成: 色情報の取得に使用するkd木を作成します
- 色データの収集: 彩色処理をやりやすくなるように、元データの色情報を変換しておきます
ソースコード的にはこの辺り↓ (convert2block.py
)
# Initial procedure
self.__calc_decimated()
self.__build_src_kd()
self.__create_color_dict()
Octreeの作成とメッシュ交錯判定
Octreeは立方体領域を、xyz方向にそれぞれ2分割(合計8分割)し、8つの立方体領域に分割するという考え方です。プログラム内では、create_new_octree(box)
というメソッドで行っています。
Octreeで立方体を作成しつつ、それぞれの立方体が元データのメッシュと交錯するかの判定を行います。交錯する場合にはその立方体を残し、その立方体をさらに8分割し同様の処理を繰り返します(再帰処理)。交錯しない場合には、その立方体は無視されます。
交錯判定はBlenderの BVHTreeユーティリティのoverlap
メソッドで可能です。プログラム内では、check_if_overlap(obj, box)
というメソッドで行っています。 この部分の処理は立方体毎に独立した処理になるので、アドオン内部では並列処理されます(Pythonによるマルチプロセス処理)。また再帰的に行うことができる処理なので、Octreeの深さを引数で渡し、再帰処理を行います。
ソースコード的にはこの辺り↓ (convert2block.py
)
def invoke_create_voxel(self, obj, box, max_depth):
# Calc unit length
self.unit = (box[1].z - box[0].z) / float(2 ** max_depth)
overlap = Converter.check_if_overlap(obj, box)
if overlap:
boxes = Converter.create_new_octree(box)
jobs = []
for child in boxes:
p = Process(
target=self.create_voxel,
args=(obj, child, 1, self.voxel_list, max_depth)
)
jobs.append(p)
p.start()
[job.join() for job in jobs]
各ブロックの色情報の取得
色情報の取得には、前処理で作成したkd木を使用します。こちらはBlenderの KDTreeユーティリティのfind
メソッドを利用します。Octree処理で作成された各立方体毎に元の3Dデータの近接点の色情報を取得します。 この処理も立方体毎に独立した処理になるので、アドオン内部では並列処理されます。
ソースコード的にはこの辺り↓ (convert2block.py
)
# Find closest color
co, index, dist = self.src_kd.find(voxel[0])
if self.decimated.data.vertex_colors:
rgb = self.decimated.data.vertex_colors["Col"].data[self.color_dict[index]].color
else:
rgb = (1.0, 1.0, 1.0) # White
ブロックの描画
ここでは前のプロセスで計算された立方体の頂点と色情報をもとに、実際の3Dデータとしての色付き立方体を作成します。並行して、のちのちMinecraftに送信しやすいような形でデータ作成して戻り値として返します。また、ここの処理はBlenderの表示系に関わる部分となり、並列化出来ないので、描画する立方体の数に比例して処理時間がかかります。
Octreeを変更してブロック化処理を実行してみた結果が↓です。(ちなみに元データは メンバーページで閲覧できる僕です)既にみなさまお気づきかと思いますが、Octreeの数はtreeの深さに対して指数関数的に増大するので、増えるに従って計算量もアヒィーってなります。
転送処理
Minecraftへの転送にあたっては、mcpiというpythonモジュールを使用してBlenderからデータを送ります。サーバ側は、 Raspberry JuiceというSpigot(MinecraftのModサーバ)向けプラグインでmcpiからのリクエストを受け取ります。
- ※ mcpiはMinecraft PI Edition用にMojang ABが作成したPythonのモジュール。BlenderのPythonインタプリタはPython3系なので、今回のプログラムではPython3向けに移植された py3minepiを使用します。
- ※ Raspberry Juiceは、mcpiに対応するAPIをSpigotに埋め込むためのプラグイン
ブロックの選定
ブロック化処理で彩色された色を、Minecraftのどのブロックで再現するかにあたっては、プログラム内に予めMinecraftのブロック定義と色情報を持っておき、欲しい色に最も近い色をもつブロック(RGBベクトルの距離が最小となるブロック)を選定するという方法にしています。
ブロック定義的な部分↓(block_def.py
)
_BlockItem(
"White Wool",
Vector((0.95, 0.95, 0.95)),
(35, None)
),
_BlockItem(
"Orange Wool",
Vector((0.92, 0.53, 0.25)),
(35, 1)
),
_BlockItem(
"Magenta Wool",
Vector((0.73, 0.31, 0.77)),
(35, 2)
)
mcpiによる転送
mcpiを使えば簡単にブロックを設置可能です。具体的には、setBlock
というメソッドに設置したいブロックの位置と種類を渡せば、Minecraft上に任意のブロックを設置できます。 あとはブロック化処理で作成したブロック定義に対して、このメソッドを呼べばブロック化されたオブジェクトを転送できます。
ソースコード的にはこの辺り↓ (convert2block.py
)
for this_block in block_map:
if this_block.color:
self.mc.setBlock(
pos.x + this_block.pos[0]/2.0,
pos.y + this_block.pos[2]/2.0,
pos.z - this_block.pos[1]/2.0,
this_block.block_type,
this_block.color
)
結果
転送結果は下記のようになりました。
上記の写真をご覧いただければ分かるように、若干色がおかしな感じになっております。こちらは、Minecraftのブロックて表現できる限界なのか?という気もしておりますが、もしかしたらバグかもしれないので要継続調査です。ともあれ転送できてヨカッタ\(^o^)/。ちなみにMinecraftのブロックのサイズは変えることが出来ないので、Octreeが深いモデルほど、Minecraftに転送した時の全体のサイズは大きくなります。
おまけ
3Dプリントするとこうなります↓。これはデスクトッププリンタで出力したので、色はつきません…グヌヌ
おわりに
自分が書いたコードの結果が可視化されるというのは非常に楽しく、 昔はFlash厨だったのですが、最近はプログラムによる3Dモデリングにドハマリしてます。3Dモデルの場合、3Dプリントすれば実体化もできるという特典付きです!読者の皆様も是非!