IO: update FBX im-/exporter to use the current Action API
Use the current Action API (i.e. move away from the to-be-deleted-in-5.0 one) to import and export F-Curves from/to FBX files. There is a slight difference in functionality for the exporter, in the selection of which Actions to export for the "All Actions" option. This is just a minimal change to ensure the legacy API is no longer used. Old: `action.fcurves` was iterated, and if all FCurves could resolve to existing properties, the Action was exported. This would only work reliably for single-slotted Actions, due to the use of the deprecated `action.fcurves` property. New: the above check is done for each Channelbag in the Action. The first Channelbag that match the above check is exported. This does _not_ export all suitable channelbags; it merely improves on the old behaviour slightly. This is part of #146586 Pull Request: https://projects.blender.org/blender/blender/pulls/146980
This commit is contained in:
@@ -2491,16 +2491,25 @@ def fbx_animations(scene_data):
|
||||
|
||||
# All actions.
|
||||
if scene_data.settings.bake_anim_use_all_actions:
|
||||
def validate_actions(act, path_resolve):
|
||||
for fc in act.fcurves:
|
||||
data_path = fc.data_path
|
||||
if fc.array_index:
|
||||
data_path = data_path + "[%d]" % fc.array_index
|
||||
try:
|
||||
path_resolve(data_path)
|
||||
except ValueError:
|
||||
return False # Invalid.
|
||||
return True # Valid.
|
||||
def find_validate_action_slot(act, path_resolve) -> bpy.types.ActionSlot | None:
|
||||
for layer in act.layers:
|
||||
for strip in layer.strips:
|
||||
for channelbag in strip.channelbags:
|
||||
if not channelbag.fcurves:
|
||||
# Do not export empty Channelbags.
|
||||
continue
|
||||
for fc in channelbag.fcurves:
|
||||
data_path = fc.data_path
|
||||
if fc.array_index:
|
||||
data_path = data_path + "[%d]" % fc.array_index
|
||||
try:
|
||||
path_resolve(data_path)
|
||||
except ValueError:
|
||||
break # Invalid, go to next strip.
|
||||
else:
|
||||
# Did not 'break', so all F-Curves are valid.
|
||||
return channelbag.slot
|
||||
return None # Found nothing to return.
|
||||
|
||||
def restore_object(ob_to, ob_from):
|
||||
# Restore org state of object (ugh :/ ).
|
||||
@@ -2540,14 +2549,20 @@ def fbx_animations(scene_data):
|
||||
pbones_matrices = [pbo.matrix_basis.copy() for pbo in ob.pose.bones] if ob.type == 'ARMATURE' else ...
|
||||
|
||||
org_act = ob.animation_data.action
|
||||
org_act_slot = ob.animation_data.action_slot
|
||||
path_resolve = ob.path_resolve
|
||||
|
||||
for act in bpy.data.actions:
|
||||
# For now, *all* paths in the action must be valid for the object, to validate the action.
|
||||
# Unless that action was already assigned to the object!
|
||||
if act != org_act and not validate_actions(act, path_resolve):
|
||||
if act == org_act:
|
||||
act_slot = org_act_slot
|
||||
else:
|
||||
act_slot = find_validate_action_slot(act, path_resolve)
|
||||
if not act_slot:
|
||||
continue
|
||||
ob.animation_data.action = act
|
||||
ob.animation_data.action_slot = act_slot
|
||||
frame_start, frame_end = act.frame_range # sic!
|
||||
add_anim(animations, animated,
|
||||
fbx_animations_do(scene_data, (ob, act), frame_start, frame_end, True,
|
||||
@@ -2557,6 +2572,7 @@ def fbx_animations(scene_data):
|
||||
for pbo, mat in zip(ob.pose.bones, pbones_matrices):
|
||||
pbo.matrix_basis = mat.copy()
|
||||
ob.animation_data.action = org_act
|
||||
ob.animation_data.action_slot = org_act_slot
|
||||
restore_object(ob, ob_copy)
|
||||
scene.frame_set(scene.frame_current, subframe=0.0)
|
||||
|
||||
@@ -2564,6 +2580,7 @@ def fbx_animations(scene_data):
|
||||
for pbo, mat in zip(ob.pose.bones, pbones_matrices):
|
||||
pbo.matrix_basis = mat.copy()
|
||||
ob.animation_data.action = org_act
|
||||
ob.animation_data.action_slot = org_act_slot
|
||||
|
||||
bpy.data.objects.remove(ob_copy)
|
||||
scene.frame_set(scene.frame_current, subframe=0.0)
|
||||
|
||||
@@ -17,6 +17,7 @@ if "bpy" in locals():
|
||||
import bpy
|
||||
from bpy.app.translations import pgettext_tip as tip_
|
||||
from mathutils import Matrix, Euler, Vector, Quaternion
|
||||
from bpy_extras import anim_utils
|
||||
|
||||
# Also imported in .fbx_utils, so importing here is unlikely to further affect Blender startup time.
|
||||
import numpy as np
|
||||
@@ -893,10 +894,10 @@ def blen_store_keyframes_multi(fbx_key_times, fcurve_and_key_values_pairs, blen_
|
||||
blen_fcurve.update()
|
||||
|
||||
|
||||
def blen_read_animations_action_item(action, item, cnodes, fps, anim_offset, global_scale, shape_key_deforms,
|
||||
def blen_read_animations_action_item(channelbag, item, cnodes, fps, anim_offset, global_scale, shape_key_deforms,
|
||||
fbx_ktime):
|
||||
"""
|
||||
'Bake' loc/rot/scale into the action,
|
||||
'Bake' loc/rot/scale into the channelbag,
|
||||
taking any pre_ and post_ matrix into account to transform from fbx into blender space.
|
||||
"""
|
||||
from bpy.types import ShapeKey, Material, Camera
|
||||
@@ -948,7 +949,7 @@ def blen_read_animations_action_item(action, item, cnodes, fps, anim_offset, glo
|
||||
else: # Euler
|
||||
props[1] = (bl_obj.path_from_id("rotation_euler"), 3, grpname or "Euler Rotation")
|
||||
|
||||
blen_curves = [action.fcurves.new(prop, index=channel, action_group=grpname)
|
||||
blen_curves = [channelbag.fcurves.new(prop, index=channel, group_name=grpname)
|
||||
for prop, nbr_channels, grpname in props for channel in range(nbr_channels)]
|
||||
|
||||
if isinstance(item, Material):
|
||||
@@ -1115,7 +1116,8 @@ def blen_read_animations(fbx_tmpl_astack, fbx_tmpl_alayer, stacks, scene, anim_o
|
||||
id_data.animation_data.action_slot = action.slots[0]
|
||||
|
||||
# And actually populate the action!
|
||||
blen_read_animations_action_item(action, item, cnodes, scene.render.fps, anim_offset, global_scale,
|
||||
channelbag = anim_utils.action_ensure_channelbag_for_slot(action, action.slots[0])
|
||||
blen_read_animations_action_item(channelbag, item, cnodes, scene.render.fps, anim_offset, global_scale,
|
||||
shape_key_values, fbx_ktime)
|
||||
|
||||
# If the minimum/maximum animated value is outside the slider range of the shape key, attempt to expand the slider
|
||||
|
||||
Reference in New Issue
Block a user