mirror of
https://github.com/godotengine/godot-blender-exporter.git
synced 2026-01-05 18:10:04 +03:00
251 lines
9.5 KiB
Python
251 lines
9.5 KiB
Python
"""Parsing Blender animation_data to create appropriate Godot
|
|
AnimationPlayer as well as distribute Blender action into various
|
|
action exporting functions"""
|
|
|
|
import bpy
|
|
import mathutils
|
|
from .action import (
|
|
export_camera_action,
|
|
export_shapekey_action,
|
|
export_light_action,
|
|
export_constrained_xform_action,
|
|
export_transform_action
|
|
)
|
|
from .constraint_baking import (
|
|
check_object_constraint,
|
|
check_pose_constraint,
|
|
)
|
|
from .serializer import get_animation_player
|
|
from .action import ActionStrip
|
|
|
|
|
|
ACTION_EXPORTER_MAP = {
|
|
'transform': export_transform_action,
|
|
'shapekey': export_shapekey_action,
|
|
'light': export_light_action,
|
|
'camera': export_camera_action,
|
|
}
|
|
|
|
|
|
class ObjectAnimationExporter:
|
|
"""A helper class holding states while exporting
|
|
animation data from a blender object"""
|
|
|
|
def __init__(self, godot_node, blender_object, action_type):
|
|
self.godot_node = godot_node
|
|
self.blender_object = blender_object
|
|
|
|
self.animation_player = None
|
|
|
|
self.need_baking = False
|
|
|
|
self.unmute_nla_tracks = []
|
|
self.mute_nla_tracks = []
|
|
|
|
self.check_baking_condition(action_type)
|
|
self.preprocess_nla_tracks(blender_object)
|
|
|
|
if not self.need_baking:
|
|
self.action_exporter_func = ACTION_EXPORTER_MAP[action_type]
|
|
else:
|
|
self.action_exporter_func = export_constrained_xform_action
|
|
|
|
def check_baking_condition(self, action_type):
|
|
"""Check whether the animated object has any constraint and
|
|
thus need to do baking, if needs, some states would be set"""
|
|
if action_type == 'transform':
|
|
has_obj_cst = check_object_constraint(self.blender_object)
|
|
has_pose_cst = check_pose_constraint(self.blender_object)
|
|
has_non_inherit_bone = False
|
|
if isinstance(self.blender_object.data, bpy.types.Armature):
|
|
for rbone in self.blender_object.data.bones:
|
|
if (rbone.use_inherit_rotation is False or
|
|
rbone.inherit_scale == 'NONE' or
|
|
rbone.inherit_scale == 'NONE_LEGACY'):
|
|
has_non_inherit_bone = True
|
|
break
|
|
self.need_baking = (
|
|
has_obj_cst or has_pose_cst or has_non_inherit_bone)
|
|
|
|
def preprocess_nla_tracks(self, blender_object):
|
|
"""Iterative through nla tracks, separately store mute and unmuted
|
|
tracks"""
|
|
if blender_object.animation_data:
|
|
for nla_track in blender_object.animation_data.nla_tracks:
|
|
if not nla_track.strips:
|
|
# skip empty tracks
|
|
continue
|
|
if not nla_track.mute:
|
|
self.unmute_nla_tracks.append(nla_track)
|
|
else:
|
|
self.mute_nla_tracks.append(nla_track)
|
|
|
|
def export_active_action(self, escn_file, export_settings, active_action):
|
|
"""Export the active action, if needed, would call bake.
|
|
Note that active_action maybe None, which would happen when object has
|
|
some constraint (so even no action it is still animated)"""
|
|
if active_action is None:
|
|
# object has constraints on other objects
|
|
assert self.need_baking
|
|
anim_rsc_name = self.blender_object.name + 'Action'
|
|
else:
|
|
anim_rsc_name = active_action.name
|
|
|
|
if self.animation_player.active_animation is None:
|
|
self.animation_player.add_active_animation_resource(
|
|
escn_file, anim_rsc_name
|
|
)
|
|
|
|
self.action_exporter_func(
|
|
self.godot_node,
|
|
export_settings,
|
|
self.blender_object,
|
|
ActionStrip(active_action),
|
|
self.animation_player.active_animation
|
|
)
|
|
self.clear_action_effect()
|
|
|
|
if not self.need_baking:
|
|
# here export unmuted nla_tracks into animation resource,
|
|
# this is not needed for baking, as baking has applied to
|
|
# active action
|
|
for track in self.unmute_nla_tracks:
|
|
for strip in track.strips:
|
|
if strip.action:
|
|
self.action_exporter_func(
|
|
self.godot_node,
|
|
export_settings,
|
|
self.blender_object,
|
|
ActionStrip(strip),
|
|
self.animation_player.active_animation
|
|
)
|
|
self.clear_action_effect()
|
|
|
|
def export_active_action_from_nla(self, escn_file, export_settings):
|
|
"""Export all unmute nla_tracks into an active action.
|
|
Note that it would not do baking for constraint"""
|
|
if self.animation_player.active_animation is None:
|
|
self.animation_player.add_active_animation_resource(
|
|
escn_file, self.blender_object.name + 'Action'
|
|
)
|
|
|
|
for track in self.unmute_nla_tracks:
|
|
for strip in track.strips:
|
|
if strip.action:
|
|
self.action_exporter_func(
|
|
self.godot_node,
|
|
export_settings,
|
|
self.blender_object,
|
|
ActionStrip(strip),
|
|
self.animation_player.active_animation
|
|
)
|
|
self.clear_action_effect()
|
|
|
|
def export_stashed_track(self, escn_file, export_settings, stashed_track):
|
|
"""Export a muted nla_track, track with all its contained action
|
|
is exported to a single animation_resource.
|
|
It works as an action lib"""
|
|
if not stashed_track.strips:
|
|
return
|
|
|
|
# if only one action in nla_track, user may not editted
|
|
# nla_track name, thus this would make exported name nicer
|
|
if len(stashed_track.strips) > 1:
|
|
anim_name = stashed_track.name
|
|
else:
|
|
anim_name = stashed_track.strips[0].name
|
|
|
|
anim_resource = self.animation_player.create_animation_resource(
|
|
escn_file, anim_name
|
|
)
|
|
|
|
if self.need_baking:
|
|
stashed_track.mute = False
|
|
|
|
for strip in stashed_track.strips:
|
|
if strip.action:
|
|
self.action_exporter_func(
|
|
self.godot_node,
|
|
export_settings,
|
|
self.blender_object,
|
|
ActionStrip(strip),
|
|
anim_resource
|
|
)
|
|
self.clear_action_effect()
|
|
|
|
if self.need_baking:
|
|
stashed_track.mute = True
|
|
else: # not self.need_baking:
|
|
# if baking, nla_tracks are already baked into strips
|
|
for nla_track in self.unmute_nla_tracks:
|
|
for strip in nla_track.strips:
|
|
if strip.action:
|
|
self.action_exporter_func(
|
|
self.godot_node,
|
|
export_settings,
|
|
self.blender_object,
|
|
ActionStrip(strip),
|
|
anim_resource
|
|
)
|
|
self.clear_action_effect()
|
|
|
|
def clear_action_effect(self):
|
|
"""Clear side effect of exporting an action"""
|
|
if (isinstance(self.blender_object, bpy.types.Object) and
|
|
self.blender_object.pose is not None):
|
|
for pose_bone in self.blender_object.pose.bones:
|
|
rest_bone = pose_bone.bone
|
|
pose_bone.matrix_basis = mathutils.Matrix.Identity(4)
|
|
|
|
|
|
def export_animation_data(escn_file, export_settings, godot_node,
|
|
blender_object, action_type):
|
|
"""Export the action and nla_tracks in blender_object.animation_data,
|
|
it will further call the action exporting function in AnimationDataExporter
|
|
given by `func_name`"""
|
|
if not export_settings['use_export_animation']:
|
|
return
|
|
|
|
anim_exporter = ObjectAnimationExporter(
|
|
godot_node, blender_object, action_type
|
|
)
|
|
|
|
if (blender_object.animation_data is None and
|
|
not anim_exporter.need_baking):
|
|
return
|
|
|
|
anim_exporter.animation_player = get_animation_player(
|
|
escn_file, export_settings, godot_node
|
|
)
|
|
|
|
# back up active action to reset back after finish exporting
|
|
if blender_object.animation_data:
|
|
active_action = blender_object.animation_data.action
|
|
else:
|
|
active_action = None
|
|
|
|
if (active_action is not None or anim_exporter.need_baking):
|
|
anim_exporter.export_active_action(
|
|
escn_file, export_settings, active_action)
|
|
elif anim_exporter.unmute_nla_tracks:
|
|
# if has effective nla_tracks but no active action, fake one
|
|
anim_exporter.export_active_action_from_nla(
|
|
escn_file, export_settings)
|
|
|
|
# export actions in nla_tracks, each exported to seperate
|
|
# animation resources
|
|
if export_settings['use_stashed_action']:
|
|
if blender_object.animation_data:
|
|
# clear active action, isolate NLA track
|
|
blender_object.animation_data.action = None
|
|
obj_use_nla_backup = blender_object.animation_data.use_nla
|
|
blender_object.animation_data.use_nla = True
|
|
for stashed_track in anim_exporter.mute_nla_tracks:
|
|
anim_exporter.export_stashed_track(
|
|
escn_file, export_settings, stashed_track)
|
|
if blender_object.animation_data:
|
|
blender_object.animation_data.use_nla = obj_use_nla_backup
|
|
|
|
if blender_object.animation_data is not None:
|
|
blender_object.animation_data.action = active_action
|