Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ShapeKeys driver export #808

Merged
merged 6 commits into from
Dec 14, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 12 additions & 0 deletions addons/io_scene_gltf2/blender/com/gltf2_blender_conversion.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,15 @@ def texture_transform_gltf_to_blender(texture_transform):
'rotation': rotation,
'scale': [scale[0], scale[1]],
}

def get_target(property):
return {
"delta_location": "translation",
"delta_rotation_euler": "rotation",
"location": "translation",
"rotation_axis_angle": "rotation",
"rotation_euler": "rotation",
"rotation_quaternion": "rotation",
"scale": "scale",
"value": "weights"
}.get(property)
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,14 @@ def gather_animation_channel_target(channels: typing.Tuple[bpy.types.FCurve],
blender_object: bpy.types.Object,
bake_bone: typing.Union[str, None],
bake_channel: typing.Union[str, None],
driver_obj,
export_settings
) -> gltf2_io.AnimationChannelTarget:

return gltf2_io.AnimationChannelTarget(
extensions=__gather_extensions(channels, blender_object, export_settings, bake_bone),
extras=__gather_extras(channels, blender_object, export_settings, bake_bone),
node=__gather_node(channels, blender_object, export_settings, bake_bone),
node=__gather_node(channels, blender_object, export_settings, bake_bone, driver_obj),
path=__gather_path(channels, blender_object, export_settings, bake_bone, bake_channel)
)

Expand All @@ -55,8 +56,13 @@ def __gather_extras(channels: typing.Tuple[bpy.types.FCurve],
def __gather_node(channels: typing.Tuple[bpy.types.FCurve],
blender_object: bpy.types.Object,
export_settings,
bake_bone: typing.Union[str, None]
bake_bone: typing.Union[str, None],
driver_obj
) -> gltf2_io.Node:

if driver_obj is not None:
return gltf2_blender_gather_nodes.gather_node(driver_obj, None, export_settings)

if blender_object.type == "ARMATURE":
# TODO: get joint from fcurve data_path and gather_joint

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from io_scene_gltf2.blender.exp import gltf2_blender_gather_animation_channel_target
from io_scene_gltf2.blender.exp import gltf2_blender_get
from io_scene_gltf2.blender.exp import gltf2_blender_gather_skins
from io_scene_gltf2.blender.exp import gltf2_blender_gather_drivers


@cached
Expand Down Expand Up @@ -76,18 +77,41 @@ def gather_animation_channels(blender_action: bpy.types.Action,
p,
bake_range_start,
bake_range_end,
blender_action.name)
blender_action.name,
None)
channels.append(channel)


# Retrieve channels for drivers, if needed
drivers_to_manage = gltf2_blender_gather_drivers.get_sk_drivers(blender_object)
for obj, fcurves in drivers_to_manage:
channel = __gather_animation_channel(
fcurves,
blender_object,
export_settings,
None,
None,
bake_range_start,
bake_range_end,
blender_action.name,
obj)
channels.append(channel)

else:
for channel_group in __get_channel_groups(blender_action, blender_object, export_settings):
channel_group_sorted = __get_channel_group_sorted(channel_group, blender_object)
if len(channel_group_sorted) == 0:
# Only errors on channels, ignoring
continue
channel = __gather_animation_channel(channel_group_sorted, blender_object, export_settings, None, None, bake_range_start, bake_range_end, blender_action.name)
channel = __gather_animation_channel(channel_group_sorted, blender_object, export_settings, None, None, bake_range_start, bake_range_end, blender_action.name, None)
if channel is not None:
channels.append(channel)


# resetting driver caches
gltf2_blender_gather_drivers.get_sk_driver_values.reset_cache()
gltf2_blender_gather_drivers.get_sk_drivers.reset_cache()

return channels

def __get_channel_group_sorted(channels: typing.Tuple[bpy.types.FCurve], blender_object: bpy.types.Object):
Expand Down Expand Up @@ -144,16 +168,17 @@ def __gather_animation_channel(channels: typing.Tuple[bpy.types.FCurve],
bake_channel: typing.Union[str, None],
bake_range_start,
bake_range_end,
action_name: str
action_name: str,
driver_obj
) -> typing.Union[gltf2_io.AnimationChannel, None]:
if not __filter_animation_channel(channels, blender_object, export_settings):
return None

return gltf2_io.AnimationChannel(
extensions=__gather_extensions(channels, blender_object, export_settings, bake_bone),
extras=__gather_extras(channels, blender_object, export_settings, bake_bone),
sampler=__gather_sampler(channels, blender_object, export_settings, bake_bone, bake_channel, bake_range_start, bake_range_end, action_name),
target=__gather_target(channels, blender_object, export_settings, bake_bone, bake_channel)
sampler=__gather_sampler(channels, blender_object, export_settings, bake_bone, bake_channel, bake_range_start, bake_range_end, action_name, driver_obj),
target=__gather_target(channels, blender_object, export_settings, bake_bone, bake_channel, driver_obj)
)


Expand Down Expand Up @@ -187,7 +212,8 @@ def __gather_sampler(channels: typing.Tuple[bpy.types.FCurve],
bake_channel: typing.Union[str, None],
bake_range_start,
bake_range_end,
action_name
action_name,
driver_obj
) -> gltf2_io.AnimationSampler:
return gltf2_blender_gather_animation_samplers.gather_animation_sampler(
channels,
Expand All @@ -197,6 +223,7 @@ def __gather_sampler(channels: typing.Tuple[bpy.types.FCurve],
bake_range_start,
bake_range_end,
action_name,
driver_obj,
export_settings
)

Expand All @@ -205,10 +232,11 @@ def __gather_target(channels: typing.Tuple[bpy.types.FCurve],
blender_object: bpy.types.Object,
export_settings,
bake_bone: typing.Union[str, None],
bake_channel: typing.Union[str, None]
bake_channel: typing.Union[str, None],
driver_obj
) -> gltf2_io.AnimationChannelTarget:
return gltf2_blender_gather_animation_channel_target.gather_animation_channel_target(
channels, blender_object, bake_bone, bake_channel, export_settings)
channels, blender_object, bake_bone, bake_channel, driver_obj, export_settings)


def __get_channel_groups(blender_action: bpy.types.Action, blender_object: bpy.types.Object, export_settings):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from io_scene_gltf2.blender.com import gltf2_blender_math
from io_scene_gltf2.blender.exp import gltf2_blender_get
from io_scene_gltf2.blender.exp import gltf2_blender_extract
from io_scene_gltf2.blender.exp.gltf2_blender_gather_drivers import get_sk_drivers, get_sk_driver_values
from . import gltf2_blender_export_keys
from io_scene_gltf2.io.com import gltf2_io_debug

Expand Down Expand Up @@ -167,6 +168,13 @@ def get_bone_matrix(blender_object_if_armature: typing.Optional[bpy.types.Object
else:
matrix = blender_object_if_armature.convert_space(pose_bone=pbone, matrix=matrix, from_space='POSE', to_space='LOCAL')
data[frame][pbone.name] = matrix


# If some drivers must be evaluated, do it here, to avoid to have to change frame by frame later
drivers_to_manage = get_sk_drivers(blender_object_if_armature)
for dr_obj, dr_fcurves in drivers_to_manage:
vals = get_sk_driver_values(dr_obj, frame, dr_fcurves)

frame += step

return data
Expand All @@ -181,10 +189,11 @@ def gather_keyframes(blender_object_if_armature: typing.Optional[bpy.types.Objec
bake_range_start,
bake_range_end,
action_name: str,
driver_obj,
export_settings
) -> typing.List[Keyframe]:
"""Convert the blender action groups' fcurves to keyframes for use in glTF."""
if bake_bone is None:
if bake_bone is None and driver_obj is None:
# Find the start and end of the whole action group
# Note: channels has some None items only for SK if some SK are not animated
ranges = [channel.range() for channel in channels if channel is not None]
Expand All @@ -200,7 +209,7 @@ def gather_keyframes(blender_object_if_armature: typing.Optional[bpy.types.Objec
# Bake the animation, by evaluating the animation for all frames
# TODO: maybe baking can also be done with FCurve.convert_to_samples

if blender_object_if_armature is not None:
if blender_object_if_armature is not None and driver_obj is None:
if bake_bone is None:
pose_bone_if_armature = gltf2_blender_get.get_object_from_datapath(blender_object_if_armature,
channels[0].data_path)
Expand Down Expand Up @@ -241,9 +250,13 @@ def gather_keyframes(blender_object_if_armature: typing.Optional[bpy.types.Objec
"scale": scale
}[target_property]
else:
# Note: channels has some None items only for SK if some SK are not animated
key.value = [c.evaluate(frame) for c in channels if c is not None]
complete_key(key, non_keyed_values)
if driver_obj is None:
# Note: channels has some None items only for SK if some SK are not animated
key.value = [c.evaluate(frame) for c in channels if c is not None]
complete_key(key, non_keyed_values)
else:
key.value = get_sk_driver_values(driver_obj, frame, channels)
complete_key(key, non_keyed_values)
keyframes.append(key)
frame += step
else:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,12 @@ def gather_animation_sampler(channels: typing.Tuple[bpy.types.FCurve],
bake_range_start,
bake_range_end,
action_name: str,
driver_obj,
export_settings
) -> gltf2_io.AnimationSampler:

blender_object_if_armature = blender_object if blender_object.type == "ARMATURE" else None
if blender_object_if_armature is not None:
if blender_object_if_armature is not None and driver_obj is None:
if bake_bone is None:
pose_bone_if_armature = gltf2_blender_get.get_object_from_datapath(blender_object_if_armature,
channels[0].data_path)
Expand All @@ -52,14 +53,15 @@ def gather_animation_sampler(channels: typing.Tuple[bpy.types.FCurve],
non_keyed_values = __gather_non_keyed_values(channels, blender_object,
blender_object_if_armature, pose_bone_if_armature,
bake_channel,
driver_obj,
export_settings)


return gltf2_io.AnimationSampler(
extensions=__gather_extensions(channels, blender_object_if_armature, export_settings, bake_bone, bake_channel),
extras=__gather_extras(channels, blender_object_if_armature, export_settings, bake_bone, bake_channel),
input=__gather_input(channels, blender_object_if_armature, non_keyed_values,
bake_bone, bake_channel, bake_range_start, bake_range_end, action_name, export_settings),
bake_bone, bake_channel, bake_range_start, bake_range_end, action_name, driver_obj, export_settings),
interpolation=__gather_interpolation(channels, blender_object_if_armature, export_settings, bake_bone, bake_channel),
output=__gather_output(channels, blender_object.matrix_parent_inverse.copy().freeze(),
blender_object_if_armature,
Expand All @@ -69,6 +71,7 @@ def gather_animation_sampler(channels: typing.Tuple[bpy.types.FCurve],
bake_range_start,
bake_range_end,
action_name,
driver_obj,
export_settings)
)

Expand All @@ -77,16 +80,23 @@ def __gather_non_keyed_values(channels: typing.Tuple[bpy.types.FCurve],
blender_object_if_armature: typing.Optional[bpy.types.Object],
pose_bone_if_armature: typing.Optional[bpy.types.PoseBone],
bake_channel: typing.Union[str, None],
driver_obj,
export_settings
) -> typing.Tuple[typing.Optional[float]]:

non_keyed_values = []

obj = blender_object if driver_obj is None else driver_obj

# Note: channels has some None items only for SK if some SK are not animated
if None not in channels:
# classic case for object TRS or bone TRS
# Or if all morph target are animated

if driver_obj is not None:
# driver of SK
return tuple([None] * len(channels))

if bake_channel is None:
target = channels[0].data_path.split('.')[-1]
else:
Expand Down Expand Up @@ -115,26 +125,26 @@ def __gather_non_keyed_values(channels: typing.Tuple[bpy.types.FCurve],
for i in range(0, length):
if bake_channel is not None:
non_keyed_values.append({
"delta_location" : blender_object.delta_location,
"delta_rotation_euler" : blender_object.delta_rotation_euler,
"location" : blender_object.location,
"rotation_axis_angle" : blender_object.rotation_axis_angle,
"rotation_euler" : blender_object.rotation_euler,
"rotation_quaternion" : blender_object.rotation_quaternion,
"scale" : blender_object.scale
"delta_location" : obj.delta_location,
"delta_rotation_euler" : obj.delta_rotation_euler,
"location" : obj.location,
"rotation_axis_angle" : obj.rotation_axis_angle,
"rotation_euler" : obj.rotation_euler,
"rotation_quaternion" : obj.rotation_quaternion,
"scale" : obj.scale
}[target][i])
elif i in indices:
non_keyed_values.append(None)
else:
if blender_object_if_armature is None:
non_keyed_values.append({
"delta_location" : blender_object.delta_location,
"delta_rotation_euler" : blender_object.delta_rotation_euler,
"location" : blender_object.location,
"rotation_axis_angle" : blender_object.rotation_axis_angle,
"rotation_euler" : blender_object.rotation_euler,
"rotation_quaternion" : blender_object.rotation_quaternion,
"scale" : blender_object.scale
"delta_location" : obj.delta_location,
"delta_rotation_euler" : obj.delta_rotation_euler,
"location" : obj.location,
"rotation_axis_angle" : obj.rotation_axis_angle,
"rotation_euler" : obj.rotation_euler,
"rotation_quaternion" : obj.rotation_quaternion,
"scale" : obj.scale
}[target][i])
else:
# TODO, this is not working if the action is not active (NLA case for example)
Expand All @@ -157,7 +167,7 @@ def __gather_non_keyed_values(channels: typing.Tuple[bpy.types.FCurve],
if object_path:
shapekeys_idx = {}
cpt_sk = 0
for sk in blender_object.data.shape_keys.key_blocks:
for sk in obj.data.shape_keys.key_blocks:
if sk == sk.relative_key:
continue
if sk.mute is True:
Expand All @@ -167,7 +177,7 @@ def __gather_non_keyed_values(channels: typing.Tuple[bpy.types.FCurve],

for idx_c, channel in enumerate(channels):
if channel is None:
non_keyed_values.append(blender_object.data.shape_keys.key_blocks[shapekeys_idx[idx_c]].value)
non_keyed_values.append(obj.data.shape_keys.key_blocks[shapekeys_idx[idx_c]].value)
else:
non_keyed_values.append(None)

Expand Down Expand Up @@ -200,6 +210,7 @@ def __gather_input(channels: typing.Tuple[bpy.types.FCurve],
bake_range_start,
bake_range_end,
action_name,
driver_obj,
export_settings
) -> gltf2_io.Accessor:
"""Gather the key time codes."""
Expand All @@ -211,6 +222,7 @@ def __gather_input(channels: typing.Tuple[bpy.types.FCurve],
bake_range_start,
bake_range_end,
action_name,
driver_obj,
export_settings)
times = [k.seconds for k in keyframes]

Expand Down Expand Up @@ -262,6 +274,7 @@ def __gather_output(channels: typing.Tuple[bpy.types.FCurve],
bake_range_start,
bake_range_end,
action_name,
driver_obj,
export_settings
) -> gltf2_io.Accessor:
"""Gather the data of the keyframes."""
Expand All @@ -273,6 +286,7 @@ def __gather_output(channels: typing.Tuple[bpy.types.FCurve],
bake_range_start,
bake_range_end,
action_name,
driver_obj,
export_settings)
if bake_bone is not None:
target_datapath = "pose.bones['" + bake_bone + "']." + bake_channel
Expand Down