Git Hookにより自動でBlenderのレンダリング・FBXエクスポートを行う

## はじめに

本記事は[三重大学 計算研 Advent Calendar 2019](https://qiita.com/advent-calendar/2019/calc-mie) 二日目です。

サークルでゲーム開発をする際に、Blenderで制作した3DモデルデータもGitでバージョン管理しました。
ゲーム開発でBlenderのファイルを使うためにはFBXにエクスポートしたものを使うほうが都合が良いです。

そこで今回Git Hookという機能を用いることで、`Git Push`を実行した際に自動で**サムネイル用画像レンダリング** ・ **FBXエクスポート**が行われるようにしました。

これにより、作業が自動化できただけでなく、**FBXエクスポートの設定ミスが無くなる**、**Blendファイルを編集した際のFBXファイルの更新忘れが無くなる**というメリットもありました。

今回はGit Hookについての紹介と実際にBlenderにて自動エクスポートを行うための作業について紹介します。

## Git Hookとは

Gitの標準の機能であり、特定のGitの作業を行った際にスクリプトを実行できるものです。

種類としては

* クライアント
    * pre-commit : コミットメッセージ入力直前にスクリプト実行
    * prepare-commit-msg : コミットメッセージ入力用エディタ起動直前にスクリプト実行。デフォルトメッセージを編集したりできる
    * commit-msg : コミットメッセージ編集後にスクリプト実行
    * **pre-push** : Push実行直前にスクリプト実行　← 今回はこれを用いる
    * pre-rebase : rebase実行直前にスクリプト実行。ゼロ以外を返せば処理を中断できる。

などがあります。

これらを使えば、

* コミットする前に自動でテスト等を行ったり(pre-commit)
* デフォルトのコミットメッセージを変更したり(prepare-commit-msg)
* 入力されたコミットメッセージが規定にそっているかを調べたり(commit-msg)

が出来ます。

他にもサーバーサイドのもありますが、今回はクライアントサイドの**pre-push**を用いて、`Git Push`を行った際に自動でレンダリング、エクスポートを行えるようにします。

## 今回実現すること

developブランチにて、Blendファイルを配置し`Git Push`すると自動で

 * 画像レンダリング
 * FBX生成
 * masterブランチに生成したFBXファイルを配置しコミット

が行われるようになります。

想定されるディレクトリ構成は以下のようなものです。

```
<Push前のDevelopブランチ>

├─Scripts : 自動レンダリング、FBXエクスポート用のスクリプトを置く。具体的な内容は後述する。
└─ProjectName
    │
    ├─blend : blendファイルを置くディレクトリ
    │  │ aaa.blend
    │  │ bbb.blend
    │  │ ccc.blend
    │
    └─Textures : テクスチャを置くディレクトリ

<Push後のDevelopブランチ>

├─Scripts
└─ProjectName
    │  aaa.fbx : [自動生成]blend/aaa.blendをFBXエクスポートしたもの
    │  bbb.fbx : [自動生成]blend/bbb.blendをFBXエクスポートしたもの
    │  ccc.fbx : [自動生成]blend/ccc.blendをFBXエクスポートしたもの
    │
    ├─blend 
    │  │ aaa.blend
    │  │ bbb.blend
    │  │ ccc.blend
    │
    ├─image : [自動生成]レンダリング画像が置かれるディレクトリ
    └─Textures : テクスチャを置くディレクトリ

<Push後のmasterブランチ>

└─ProjectName
    │  aaa.fbx 
    │  bbb.fbx 
    │  ccc.fbx 
    │
    └─Textures
```

developブランチにてBlendファイルとテクスチャファイルを配置し、Pushすると、
自動でレンダリング、FBX生成がされ、
FBXファイルとテクスチャファイルをmasterにCheckoutします。

## 開発環境

今回はWindows 10にて、

* Blender 2.8
* Git version 2.23.0.windows.1

で行いました。


## 作業手順

### 1. BlenderにPathを通す

まずは、Blender.exeにPathを通します。
環境変数のPathにBlenderのインストールディレクトリを追加します。
Blender公式サイトからインストーラーをダウンロードしてきてインストールした場合は、
"C:\Program Files\Blender Foundation\Blender"
がインストールディレクトリになると思います。

環境変数の編集方法は
https://qiita.com/sta/items/6d29da0dc7069ffaae60
が参考になると思います。

### 2. レンダリングを行うためのスクリプト

次に、レンダリングを行うためのスクリプトとして、`Scripts/render.py`に以下の内容で保存します。

```python
import bpy

bpy.context.scene.render.resolution_x = 1920
bpy.context.scene.render.resolution_y = 1080
bpy.context.scene.render.resolution_percentage = 25

path = bpy.context.blend_data.filepath.rstrip(bpy.path.basename(bpy.context.blend_data.filepath)) + "../image/" +  bpy.path.basename(bpy.context.blend_data.filepath).split(".")[0] +".png"
bpy.data.scenes["Scene"].render.filepath = path

bpy.ops.render.render( write_still=True )
```

これにより、blend/aaa.blendであれば、image/aaa.pngにレンダリング結果が保存されます。

今回はサムネイル画像用のレンダリングなので解像度を1920x1080の25%に指定しています。

### 3. FBXエクスポートを行うためのスクリプト

次に、FBXエクスポートを行うためのスクリプトとして、`Scripts/ExportFBX.py`に以下の内容で保存します。

```python
import bpy,sys

def fbx_export_geometry(arg_filepath='./export.fbx'):
  bpy.ops.export_scene.fbx(
    filepath=arg_filepath,
    object_types={'ARMATURE', 'MESH'},
    bake_anim=False,
  )
  # 出力対象の種別
  #  'EMPTY'：エンプティ
  #  'CAMERA'：カメラ
  #  'LAMP'：ランプ
  #  'ARMATURE'：アーマチュア
  #  'MESH'：メッシュ
  #  'OTHER'：その他
  return

# 関数の実行
path = bpy.context.blend_data.filepath.rstrip(bpy.path.basename(bpy.context.blend_data.filepath)) + "../" +  bpy.path.basename(bpy.context.blend_data.filepath).split(".")[0] +".fbx"
fbx_export_geometry(path)
```

これにより、blend/aaa.blendであれば、aaa.fbxにFBXエクスポートされます。

また、ここでカメラやランプを除いて、アーマチュアとメッシュのみをFBXエクスポートしています。

### 4. ２つのPythonスクリプトを呼び出すためのスクリプト

それでは、この2つのPythonスクリプトを呼び出すためのスクリプトを用意します。

`Scripts/convert.sh`に以下の内容で保存します。

```bash
#!/bin/bash

log=$(pwd)/gitPush.sh.log

echo "[$(date)] start" >> $log

echo "[Messeage by convert.sh]cd git top level dir(" `git rev-parse --show-toplevel` ")"
cd  `git rev-parse --show-toplevel`

for raw in $(git log origin/develop..develop --stat | grep ".blend") ; do
	if [ ${raw##*.} = "blend" ]; then
		echo "[Messeage by convert.sh] blend file is ${raw}"
		echo "[Messeage by convert.sh] Start Render.py"
		blender --background ${raw} --python Scripts/Render.py
		echo "[Messeage by convert.sh] Start ExportFBX.py"
		blender --background ${raw} --python Scripts/ExportFBX.py
	fi	
done
echo '[Messeage by convert.sh] git commit -m "Auto Generate FBX/render image"'
git add *.fbx
git add *.png
git commit -am "Auto convert FBX and render image"
```

ログ用のechoの行が挟まっていますが、内容としては、

1. Gitのトップレベルディレクトリに移動
2. gitのlogから、変更が加わったblendファイルの名前を取得
3. 変更が加わったblendファイルに対して、画像レンダリング(Render.py)、FBXエクスポート(ExportFBX.py)を実行する
4. FBXファイルとレンダリングpng画像を`git add`する
5. `git commit`する

となっています。

## Git Hook pre-pushの設定

最後に、これまで作成してきたスクリプトをGit Hook pre-pushにて実行されるようにします。

Gitリポジトリには`.git`ディレクトリが存在しますが、`.git/hooks/`に"pre-push"というファイル名でシェルスクリプトを保存します。
すると、Git Push実行直前にこのスクリプトが実行されるようになります。

直接ここにファイルを置いてもいいですが、`.git/hooks/`はGit監視下では無いので、シンボリックリンクを置いて作業しました。

ちなみに、WindowsのPowerShellでのシンボリックリンクの貼り方は、下のコマンドになります。

```powershell
New-Item -Type SymbolicLink .git/hooks/pre-push -Value Scripts/hooks/pre-push
```

pre-pushファイルには以下の内容で保存します。

```bash
#!/bin/sh

remote="$1"
url="$2"
z40=0000000000000000000000000000000000000000
 
while read local_ref local_sha remote_ref remote_sha
do
	if [[ "${remote_ref##refs/heads/}" = "develop" ]]; then
		`git rev-parse --show-toplevel`/Scripts/convert.sh
		git checkout master
		git checkout develop `git rev-parse --show-toplevel`'/ProjectName/*.fbx'
		git checkout develop `git rev-parse --show-toplevel`'/ProjectName/Textures/*'
		git commit -m "[Auto commit] Add FBX from develop"

		git checkout develop
		git push origin master
	fi
done

```

1. 作業しているブランチがdevelopかを調べる
2. `<Gitのトップレベルディレクトリ>/Scripts/convert.sh` を実行。(ここで自動レンダリング、FBXエクスポートを行う)
3. テクスチャファイルと、自動生成したFBXファイルをMasterブランチにcheckout
4. masterを`git commit`
5. masterを`git push`
6. 
といった内容です。

以上により、developブランチにBlendファイルとテクスチャをpushするだけで、自動で

* レンダリング
* FBXエクスポート
* developブランチにレンダリング画像とFBXをcommit
* masterブランチにFBXとテクスチャをCheckout
* masterブランチの`git commit` & `push`

が行われるようになります。

## さいごに

今回、blendファイルが更新されたら自動でFBXも更新するシステムを作ろうとして、Git Hookを使ってみました。

最初はCI/CDが必要なのかな...と思ってGitHub Actionsを試していたのですが、Blenderでレンダリングを行う際にサーバーサイドにGPUが必要なので悩んでいたところ、サークルの先輩にGit Hookを紹介してもらいました。

結果としてやりたかったことが実現でき、さらにクライアント側で完結するコンパクトなシステムになったと思います。

また、Git Hookで呼び出すためにBlender Pythonスクリプトも初めて使ってみましたが、これによりレンダリング・FBXエクスポートの設定ミスが無くなり、特にFBXエクスポートで誤ってランプやカメラを含んでしまいUnityで読み込んだ際におかしくなる問題が無くなりました。

これまでは自動化のモチベーションはあまりなかったのですが、自動化によるメリットをいくつも得れた良い機会でした。

Git Hookには他にもいろいろな発火タイミングがあるので、便利に使っていきたいと思います。



## 参考文献
 * [Git - Git フック](https://git-scm.com/book/ja/v2/Git-%E3%81%AE%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%9E%E3%82%A4%E3%82%BA-Git-%E3%83%95%E3%83%83%E3%82%AF)
 * [Blenderで利用可能なpythonスクリプトを作る その１（オブジェクトの削除、エクスポート）](http://bluebirdofoz.hatenablog.com/entry/2018/04/23/000656)
 * [Blenderのpythonスクリプトで引数を取得する](http://bluebirdofoz.hatenablog.com/entry/2019/07/21/213610)