FBX IO: add support for import & export of camera focal length animation.
Requested in T54050, usually would not add new stuff to FBX but this looked like totally needed for compo needs...
This commit is contained in:
parent
7e18281d2f
commit
b890f0d7e8
Notes:
blender-bot
2023-02-14 06:11:19 +01:00
Referenced by issue blender/blender#54050: Alembic: Camera properties (focal length, focus distance, ...) animation not importing Referenced by issue blender/blender#54050, Alembic: Camera properties (focal length, focus distance, ...) animation not importing
|
@ -1896,8 +1896,9 @@ def fbx_animations_do(scene_data, ref_id, f_start, f_end, start_zero, objects=No
|
|||
ACNW(ob_obj.key, 'LCL_SCALING', force_key, force_sek, scale))
|
||||
p_rots[ob_obj] = rot
|
||||
|
||||
animdata_shapes = OrderedDict()
|
||||
force_key = (simplify_fac == 0.0)
|
||||
|
||||
animdata_shapes = OrderedDict()
|
||||
for me, (me_key, _shapes_key, shapes) in scene_data.data_deformers_shape.items():
|
||||
# Ignore absolute shape keys for now!
|
||||
if not me.shape_keys.use_relative:
|
||||
|
@ -1908,6 +1909,12 @@ def fbx_animations_do(scene_data, ref_id, f_start, f_end, start_zero, objects=No
|
|||
acnode.add_group(me_key, shape.name, shape.name, (shape.name,))
|
||||
animdata_shapes[channel_key] = (acnode, me, shape)
|
||||
|
||||
animdata_cameras = OrderedDict()
|
||||
for cam_obj, cam_key in scene_data.data_cameras.items():
|
||||
cam = cam_obj.bdata.data
|
||||
acnode = AnimationCurveNodeWrapper(cam_key, 'CAMERA_FOCAL', force_key, force_sek, (cam.lens,))
|
||||
animdata_cameras[cam_key] = (acnode, cam)
|
||||
|
||||
currframe = f_start
|
||||
while currframe <= f_end:
|
||||
real_currframe = currframe - f_start if start_zero else currframe
|
||||
|
@ -1927,6 +1934,8 @@ def fbx_animations_do(scene_data, ref_id, f_start, f_end, start_zero, objects=No
|
|||
ob_obj.dupli_list_clear()
|
||||
for anim_shape, me, shape in animdata_shapes.values():
|
||||
anim_shape.add_keyframe(real_currframe, (shape.value * 100.0,))
|
||||
for anim_camera, camera in animdata_cameras.values():
|
||||
anim_camera.add_keyframe(real_currframe, (camera.lens,))
|
||||
currframe += bake_step
|
||||
|
||||
scene.frame_set(back_currframe, 0.0)
|
||||
|
@ -1958,6 +1967,18 @@ def fbx_animations_do(scene_data, ref_id, f_start, f_end, start_zero, objects=No
|
|||
anim_data = animations[elem_key] = ("dummy_unused_key", OrderedDict())
|
||||
anim_data[1][fbx_group] = (group_key, group, fbx_gname)
|
||||
|
||||
# And cameras' lens keys.
|
||||
for cam_key, (anim_camera, camera) in animdata_cameras.items():
|
||||
final_keys = OrderedDict()
|
||||
anim_camera.simplify(simplify_fac, bake_step, force_keep)
|
||||
if not anim_camera:
|
||||
continue
|
||||
for elem_key, group_key, group, fbx_group, fbx_gname in anim_camera.get_final_data(scene, ref_id, force_keep):
|
||||
anim_data = animations.get(elem_key)
|
||||
if anim_data is None:
|
||||
anim_data = animations[elem_key] = ("dummy_unused_key", OrderedDict())
|
||||
anim_data[1][fbx_group] = (group_key, group, fbx_gname)
|
||||
|
||||
astack_key = get_blender_anim_stack_key(scene, ref_id)
|
||||
alayer_key = get_blender_anim_layer_key(scene, ref_id)
|
||||
name = (get_blenderID_name(ref_id) if ref_id else scene.name).encode()
|
||||
|
|
|
@ -729,6 +729,7 @@ class AnimationCurveNodeWrapper:
|
|||
'LCL_ROTATION': ("Lcl Rotation", "R", ("X", "Y", "Z")),
|
||||
'LCL_SCALING': ("Lcl Scaling", "S", ("X", "Y", "Z")),
|
||||
'SHAPE_KEY': ("DeformPercent", "DeformPercent", ("DeformPercent",)),
|
||||
'CAMERA_FOCAL': ("FocalLength", "FocalLength", ("FocalLength",)),
|
||||
}
|
||||
|
||||
def __init__(self, elem_key, kind, force_keying, force_startend_keying, default_values=...):
|
||||
|
|
|
@ -557,7 +557,7 @@ def blen_read_animations_action_item(action, item, cnodes, fps, anim_offset):
|
|||
'Bake' loc/rot/scale into the action,
|
||||
taking any pre_ and post_ matrix into account to transform from fbx into blender space.
|
||||
"""
|
||||
from bpy.types import Object, PoseBone, ShapeKey, Material
|
||||
from bpy.types import Object, PoseBone, ShapeKey, Material, Camera
|
||||
from itertools import chain
|
||||
|
||||
fbx_curves = []
|
||||
|
@ -577,6 +577,8 @@ def blen_read_animations_action_item(action, item, cnodes, fps, anim_offset):
|
|||
props = [("diffuse_color", 3, grpname or "Diffuse Color")]
|
||||
elif isinstance(item, ShapeKey):
|
||||
props = [(item.path_from_id("value"), 1, "Key")]
|
||||
elif isinstance(item, Camera):
|
||||
props = [(item.path_from_id("lens"), 1, "Camera")]
|
||||
else: # Object or PoseBone:
|
||||
if item.is_bone:
|
||||
bl_obj = item.bl_obj.pose.bones[item.bl_bone]
|
||||
|
@ -624,6 +626,17 @@ def blen_read_animations_action_item(action, item, cnodes, fps, anim_offset):
|
|||
for fc, v in zip(blen_curves, (value,)):
|
||||
fc.keyframe_points.insert(frame, v, {'NEEDED', 'FAST'}).interpolation = 'LINEAR'
|
||||
|
||||
elif isinstance(item, Camera):
|
||||
for frame, values in blen_read_animations_curves_iter(fbx_curves, anim_offset, 0, fps):
|
||||
value = 0.0
|
||||
for v, (fbxprop, channel, _fbx_acdata) in values:
|
||||
assert(fbxprop == b'FocalLength')
|
||||
assert(channel == 0)
|
||||
value = v
|
||||
|
||||
for fc, v in zip(blen_curves, (value,)):
|
||||
fc.keyframe_points.insert(frame, v, {'NEEDED', 'FAST'}).interpolation = 'LINEAR'
|
||||
|
||||
else: # Object or PoseBone:
|
||||
if item.is_bone:
|
||||
bl_obj = item.bl_obj.pose.bones[item.bl_bone]
|
||||
|
@ -686,7 +699,7 @@ def blen_read_animations(fbx_tmpl_astack, fbx_tmpl_alayer, stacks, scene, anim_o
|
|||
Only the first found action is linked to objects, more complex setups are not handled,
|
||||
it's up to user to reproduce them!
|
||||
"""
|
||||
from bpy.types import ShapeKey, Material
|
||||
from bpy.types import ShapeKey, Material, Camera
|
||||
|
||||
actions = {}
|
||||
for as_uuid, ((fbx_asdata, _blen_data), alayers) in stacks.items():
|
||||
|
@ -698,6 +711,8 @@ def blen_read_animations(fbx_tmpl_astack, fbx_tmpl_alayer, stacks, scene, anim_o
|
|||
id_data = item
|
||||
elif isinstance(item, ShapeKey):
|
||||
id_data = item.id_data
|
||||
elif isinstance(item, Camera):
|
||||
id_data = item
|
||||
else:
|
||||
id_data = item.bl_obj
|
||||
# XXX Ignore rigged mesh animations - those are a nightmare to handle, see note about it in
|
||||
|
@ -2838,6 +2853,13 @@ def load(operator, context, filepath="",
|
|||
if keyblocks is None:
|
||||
continue
|
||||
items += [(kb, lnk_prop) for kb in keyblocks]
|
||||
elif lnk_prop == b'FocalLength': # Camera lens.
|
||||
from bpy.types import Camera
|
||||
fbx_item = fbx_table_nodes.get(n_uuid, None)
|
||||
if fbx_item is None or not isinstance(fbx_item[1], Camera):
|
||||
continue
|
||||
cam = fbx_item[1]
|
||||
items.append((cam, lnk_prop))
|
||||
elif lnk_prop == b'DiffuseColor':
|
||||
from bpy.types import Material
|
||||
fbx_item = fbx_table_nodes.get(n_uuid, None)
|
||||
|
@ -2874,7 +2896,11 @@ def load(operator, context, filepath="",
|
|||
continue
|
||||
# Note this is an infamous simplification of the compound props stuff,
|
||||
# seems to be standard naming but we'll probably have to be smarter to handle more exotic files?
|
||||
channel = {b'd|X': 0, b'd|Y': 1, b'd|Z': 2, b'd|DeformPercent': 0}.get(acn_ctype.props[3], None)
|
||||
channel = {
|
||||
b'd|X': 0, b'd|Y': 1, b'd|Z': 2,
|
||||
b'd|DeformPercent': 0,
|
||||
b'd|FocalLength': 0
|
||||
}.get(acn_ctype.props[3], None)
|
||||
if channel is None:
|
||||
continue
|
||||
curvenodes[acn_uuid][ac_uuid] = (fbx_acitem, channel)
|
||||
|
|
Loading…
Reference in New Issue