アセットバリデーション用のBlenderプラグインを作ってみる

はじめに

QualiArts Advent Calendar 2020 - Qiita 12日目の記事です。

テクニカルアーティスト室に所属している塩塚といいます。

今回はアセットのバリデーションと、Blenderプラグインについて書かせていただきます。

アセットバリデーションとは

ゲーム開発において、MayaやBlenderのようなDCCツールでアセットの作成を行い、Unityなどのゲームエンジンに組み込んで動かすことが一般的です。

各アセットが異なるツールやセクションに渡ることになるため、仕様をしっかり固め、遵守することが大切です。
しかしこの確認作業は往々にして大変です。

膨大なアセットの全ての仕様項目に関してチェックするわけですから時間がかかり、どうしても抜け漏れが発生してしまいます。
肝心のクリエイティブ作業に集中できない上、後工程でミスが発覚した場合の手戻り工数には目も当てられません。
各セクションに事情を説明して工数表を引き直して…あぁ恐ろしい...。

そこで登場するのがバリデータです。
今回は簡単なバリデータをBlenderプラグインにて実装していきます。

サンプルシーンの説明
以下のようなモーションを作成することを考えます。

f:id:yshio:20201211170431g:plain

簡素で申し訳ないですが、クレーンみたいな動きを想像してもらえるとありがたいです。

この時、青いボーンはBlender上でアニメーションとしてキーを打ち、赤いボーンはエンジン側で揺れものとして設定するとしましょう。

構成は以下の画像のようになっています。

f:id:yshio:20201211160558p:plain
つまりキーを入れるべき骨と入れるべきでない骨に分けられるわけです。
この設定を間違えてキーを入れたりしたら大変ですよね、バリデータでチェックしましょう。

 

今回のシーンでは青いボーンはアニメーションを入れ、名前はアルファベットのみ、

赤いボーンはアニメーションを入ず、アルファベットの末尾に"_s"をつけるルールとします。

環境

Blender 2.82a (2.82a 2020-03-12)

プラグインの作り方

BlenderプラグインPythonを用いて作成することができます。

標準でサンプルが付随しており、サンプル実装やAPIの確認を簡単に行う事ができます。

f:id:yshio:20201211153756p:plain 

プラグインの情報を記載

コード内にプラグインに関する情報を記載します。

今回はサンプルに付き空欄が多いですが、基本的にしっかりと記載しましょう。

# プラグインに関する情報
bl_info = {
 "name": "ValidateSample",# プラグイン名説明
"description": "ValidateSample For 2020AdventCalendar.", # 説明
"author": "salt-k2t", # 開発者
"version": (1, 0), # プラグインのバージョン
"blender": (2, 80, 0), # 対応するblenderのバージョン
"location" : "View3D > Tool Shelf > Panel, # Blender内部でのプラグインの位置づけ

"warning" : "", # 警告
"wiki_url" : "", # プラグインの説明WikiページのURL
"tracker_url" : "", # Blender Developer OrgのスレッドURL
"category" : "Development" # プラグインのカテゴリ
}

プラグインの実行方法

なるだけ気軽にアセットの状態をチェックして欲しいと思ったので、パネルから実行できる用にしました。

f:id:yshio:20201211154836p:plain

パネルにボタンを追加するためのコードがこちらです。

今回3つのボタンを作成するため、引数付きでボタンUIの生成を行っています。

class VaridateSamplePanel(bpy.types.Panel):
bl_space_type = 'VIEW_3D'
bl_region_type = 'UI'
bl_category = "ValidateSample"
bl_label = "Validate Panel"

def draw(self, context):
layout = self.layoutlayout.operator(ValidateButton.bl_idname,text=ANIMATION).validation_type = ANIMATION

ボーン名のバリデーション

ボーン名のバリデーションがこちらです。

今回は簡単にアルファベットの文字列か、アルファベットの文字列_sのみを許し、数字などを許さないものになっています。

STANDARD_JOINT_REGEX = '^[a-zA-Z]+$' # ex)sample
SIM_JOINT_REGEX = '^[a-zA-Z]+_s$' # ex)sample_s

for ob in bpy.context.scene.objects:
if ob.type == 'ARMATURE':
for bone in ob.data.bones:
standard_joint_result = re.match(STANDARD_JOINT_REGEX, bone.name)
if standard_joint_result
continue

sim_joint_result = re.match(SIM_JOINT_REGEX, bone.name)
if sim_joint_result:
continue
invalid_joints.append(bone.name)

 アニメーションのバリデーション

アニメーションのバリデーションがこちらです。

今回はキーが打たれるのは_sのボーンのみであり、またオイラー回転以外のキーは許さないものになっています。

ANIM_CHANNEL_ROTATE_REGEX = '^rotation_euler$'

for action in bpy.data.actions:
for fcu in action.fcurves:
joint_name = fcu.data_path.split('"')[1]
sim_joint_result = re.match(SIM_JOINT_REGEX,joint_name)
if sim_joint_result:
invalid_animations.append(joint_name)
continue
channel = fcu.data_path.split(".")[-1]
anim_channel_rotate_result = re.match(ANIM_CHANNEL_ROTATE_REGEX, channel)
if anim_channel_rotate_result:
continue
invalid_animations.append(joint_name)

バリデーション結果の表示

VALID もしくは INVALID : joint のような形で出力する用にしています。

実際にはエラーメッセージも表示させたほうが便利かと思います。

VALID_TEXT = "VALID"
INVALID_TEXT = "INVALID"

if invalid_items:
message = INVALID_TEXT + " : " + ",".join(invalid_items)
else:
message = VALID_TEXT
self.report({'INFO'}, message)
return {'FINISHED'}

実際に使用する

使用した動画を添付します。

ボタン1つで仕様を満たしているか確認することができます。

f:id:yshio:20201211165149g:plain

少し小さいですが、実行結果はこちらにから確認できます。

f:id:yshio:20201211162211p:plain

最後に

本記事ではアセットバリデーション用のBlenderプラグインを作成しました。

今回はBlenderを用いましたが、Mayaなど他のDCCツールでも実装することが可能であり、アセット制作効率化、不具合防止の観点からもバリデーターを作成することが有効だと思います。

皆様の参考になれば幸いです。

 

明日はk-yamさんの記事です。

おまけ 

今回のコードはgithubにて公開しています。

MITライセンスとしますので、皆様の参考になれば幸いです。

github.com

 

また先日、以下の技術記事を投稿しましたので、よかったらご覧になってください。

creator.game.cyberagent.co.jp