diff options
author | Bastien Montagne <montagne29@wanadoo.fr> | 2018-02-19 19:27:05 +0300 |
---|---|---|
committer | Bastien Montagne <montagne29@wanadoo.fr> | 2018-02-19 19:27:05 +0300 |
commit | b890f0d7e8a666cb82a908cf9b06c6b500e9e2fc (patch) | |
tree | 81453b3fdcb4b365d18abba286a96c270cd8c5d7 | |
parent | 7e18281d2f1039bf281968427fec45daa79c7531 (diff) |
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...
-rw-r--r-- | io_scene_fbx/export_fbx_bin.py | 23 | ||||
-rw-r--r-- | io_scene_fbx/fbx_utils.py | 1 | ||||
-rw-r--r-- | io_scene_fbx/import_fbx.py | 32 |
3 files changed, 52 insertions, 4 deletions
diff --git a/io_scene_fbx/export_fbx_bin.py b/io_scene_fbx/export_fbx_bin.py index d161844f..b75a8977 100644 --- a/io_scene_fbx/export_fbx_bin.py +++ b/io_scene_fbx/export_fbx_bin.py @@ -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() diff --git a/io_scene_fbx/fbx_utils.py b/io_scene_fbx/fbx_utils.py index 368e8685..82e17fe2 100644 --- a/io_scene_fbx/fbx_utils.py +++ b/io_scene_fbx/fbx_utils.py @@ -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=...): diff --git a/io_scene_fbx/import_fbx.py b/io_scene_fbx/import_fbx.py index cb67aa76..c0d7b758 100644 --- a/io_scene_fbx/import_fbx.py +++ b/io_scene_fbx/import_fbx.py @@ -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) |