Created
June 7, 2019 18:27
-
-
Save green224/ac02d61c5d0cf9446529b8b456c21775 to your computer and use it in GitHub Desktop.
Blenderでアニメーション付きモデルをちゃんとFBX出力するアドオン
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
アニメーションを全BakeしてFBX出力をするアドオン。 | |
そのままのFBX出力では、スケーリングしたボーンの子ボーンを | |
Constraintsで回転させた場合に、正常なモーションを出力できない。 | |
Bakeしようにも、デフォルトのBake処理は1アニメーションずつしかBakeできず、 | |
Bake時にConstraintsを削除する必要があるため、手作業で全Bakeするのはとても大変。 | |
このアドオンでは、それらのBake処理から出力までを一括して行う。 | |
使い方 | |
1.File->Export Baked Anim FBX を選択する | |
2.出力先ファイルを指定する | |
3.FBXが出力される。 | |
出力過程で作業状態に変更が加わってしまうので、引き続き作業を行う場合は、1回Undoをする | |
注意 | |
・出力対象のアニメーションは、NLAトラックのみ。 | |
Actionは出力されないので、出力したいアニメーションはNLAトラック化すること。 | |
・ミュート状態のNLAトラックは出力されない仕様なので、 | |
出力したいNLAトラックのミュート状態はOFFにしておくこと。 | |
・ActionのBake機能は、0未満の時刻のFrameをBakeすることができない。 | |
したがって、NLAトラックのActionExtentsに指定するFrameは0以上の値にしておくこと。 | |
・ActionのBake範囲は、キーが打ってある範囲に限定される。 | |
したがってModifierでループアニメーション化している場合でも、必要であれば | |
Bakeしたい長さの終わり部分にダミーのキーを打っておくこと。 | |
""" | |
import bpy | |
import math | |
import os | |
from bpy.props import IntProperty, FloatProperty, EnumProperty | |
from bpy.props import FloatVectorProperty, StringProperty | |
# プラグインに関する情報 | |
bl_info = { | |
"name" : "Baked FBX Exporter", | |
"author" : "Shu", | |
"version" : (0,1), | |
"blender" : (2, 7, 9), | |
"location" : "File menu > Export Baked FBX", | |
"description" : "Export FBX with baked animation", | |
"warning" : "", | |
"wiki_url" : "", | |
"tracker_url" : "", | |
"category" : "Import-Export" | |
} | |
# メニューを登録する関数 | |
def menu_func(self, context): | |
self.layout.operator( ExpBakedFBXOpe.bl_idname ) | |
# プラグインをインストールしたときの処理 | |
def register(): | |
bpy.utils.register_class( ExpBakedFBXOpe ) | |
bpy.types.INFO_MT_file.append( menu_func ) | |
# プラグインをアンインストールしたときの処理 | |
def unregister(): | |
bpy.utils.unregister_class( ExpBakedFBXOpe ) | |
bpy.types.INFO_MT_file.remove( menu_func ) | |
# メニュー項目オペレータ | |
class ExpBakedFBXOpe(bpy.types.Operator): | |
bl_idname = "object.exp_baked_fbx" | |
bl_label = "Export Baked Anim FBX" | |
bl_options = {'REGISTER', 'UNDO'} | |
filepath = StringProperty(subtype="FILE_PATH") | |
filename = StringProperty() | |
directory = StringProperty(subtype="FILE_PATH") | |
def execute(self, context): | |
if export( context, self.filepath ): return {'FINISHED'} | |
""" | |
self.report( | |
{'INFO'}, | |
"[FilePath] %s, [FileName] %s, [Directory] %s" | |
% (self.filepath, self.filename, self.directory) | |
) | |
""" | |
return {'CANCELLED'} | |
def invoke(self, context, event): | |
context.window_manager.fileselect_add(self) | |
return {'RUNNING_MODAL'} | |
# 出力処理本体 | |
def export( context, file_name ): | |
print("[ExportFBX] Begin") | |
# 対象Armatureを取得する。1Armatureにだけ対応している。 | |
# (そもそも複数ArmatureはUnityで再生できない) | |
tgt_armature = None | |
for obj in bpy.data.objects: | |
if not obj :continue | |
if obj.type != 'ARMATURE':continue | |
tgt_armature = obj | |
break | |
if not tgt_armature: | |
report('WARNING','There is no armature') | |
return False | |
if not tgt_armature.animation_data: | |
report('WARNING','There is no animation') | |
return False | |
if not tgt_armature.animation_data.nla_tracks: | |
report('WARNING','There is no NLA tracks') | |
return False | |
# オリジナルArmatureのモードおよび、NLAトラックのミュート状態を記憶しておく | |
bpy.context.scene.objects.active = tgt_armature | |
old_mode = bpy.context.object.mode | |
bpy.ops.object.mode_set(mode='POSE') | |
old_tracks_mute = [] | |
for track in tgt_armature.animation_data.nla_tracks: old_tracks_mute.append(track.mute) | |
# 全アクションを列挙 | |
actions = [ a for a in bpy.data.actions if bool(a) & a.use_fake_user ] | |
for action_idx, action in enumerate(actions): | |
# Bakeする際にArmatureが破壊されるので、複製する | |
bpy.context.scene.objects.active = tgt_armature | |
bpy.ops.object.mode_set(mode='OBJECT') | |
bpy.ops.object.select_all(action='DESELECT') | |
bpy.context.scene.objects.active = tgt_armature | |
tgt_armature.select = True | |
# FBX出力時にConstraintsが残っていると、結局正常でないアニメーションが出力されてしまうので | |
# Constraintsが削除されたArmatureを使用する必要がある。 | |
# したがって最後のArmatureだけは、複製せずにBakeを行い、そのままFBX出力に使用する。 | |
if action_idx == len(actions)-1: | |
dup_armature = tgt_armature | |
else: | |
bpy.ops.object.duplicate_move() | |
dup_armature = bpy.context.scene.objects.active | |
dup_armature.animation_data.action = action | |
# Bakeする前にNLAトラックを全ミュートにする | |
bpy.ops.object.mode_set(mode='POSE') | |
for track in dup_armature.animation_data.nla_tracks: track.mute = True | |
# キーのないボーンのTransformをデフォルト状態にしておく | |
bpy.ops.pose.select_all(action='SELECT') | |
bpy.ops.pose.transforms_clear() | |
# アクションのキーフレーム長を計算 | |
firstFrame = 9999999 | |
lastFrame = -9999999 | |
for fcu in action.fcurves: | |
for keyframe in fcu.keyframe_points: | |
x, y = keyframe.co | |
k = math.ceil(x) | |
if k < firstFrame : firstFrame = k | |
if k > lastFrame : lastFrame = k | |
# ActionをBake | |
bpy.context.scene.update() | |
bpy.ops.nla.bake( | |
frame_start=firstFrame, | |
frame_end=lastFrame, | |
only_selected=True, | |
visual_keying=True, | |
clear_constraints=True, | |
use_current_action=True, | |
bake_types={'POSE'} | |
) | |
# Bake用の仮Armatureを削除 | |
if action_idx != len(actions)-1: | |
bpy.ops.object.mode_set(mode='OBJECT') | |
bpy.ops.object.delete( use_global=True ) | |
bpy.context.scene.update() | |
print("Action: {}, First frame: {}, Second frame: {}".format(action.name, firstFrame, lastFrame)) | |
# オリジナルArmatureのモードおよびNLAトラックのミュート状態を復元する | |
bpy.context.scene.objects.active = tgt_armature | |
bpy.ops.object.mode_set(mode=old_mode) | |
for i,track in enumerate(tgt_armature.animation_data.nla_tracks): | |
track.mute = old_tracks_mute[i] | |
bpy.context.scene.update() | |
# FBX出力 | |
mdl_filepath = bpy.data.filepath | |
mdl_directory = os.path.dirname( mdl_filepath ) | |
fbx_filepath = os.path.join( mdl_directory, file_name ) | |
bpy.ops.export_scene.fbx( | |
filepath=fbx_filepath, | |
check_existing=False, | |
axis_up='Y', | |
axis_forward='-Z', | |
filter_glob="*.fbx", | |
version='BIN7400', | |
use_selection=False, | |
global_scale=1.0, | |
bake_space_transform=True, | |
object_types={'MESH', 'ARMATURE'}, | |
use_mesh_modifiers=True, | |
mesh_smooth_type='OFF', | |
use_mesh_edges=False, | |
use_tspace=False, | |
use_custom_props=False, | |
add_leaf_bones=False, | |
primary_bone_axis='Y', | |
secondary_bone_axis='X', | |
use_armature_deform_only=True, | |
bake_anim=True, | |
bake_anim_use_all_bones=True, | |
bake_anim_use_nla_strips=True, | |
bake_anim_use_all_actions=False, | |
bake_anim_step=1.0, | |
bake_anim_simplify_factor=1.0, | |
use_anim=True, | |
use_anim_action_all=True, | |
use_default_take=True, | |
use_anim_optimize=True, | |
anim_optimize_precision=6.0, | |
path_mode='AUTO', | |
embed_textures=False, | |
batch_mode='OFF', | |
use_batch_own_dir=True, | |
use_metadata=True | |
) | |
print("[ExportFBX] Complete") | |
return True | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment