glTF exporter: fix shapekeys animation export

Animation channels must be sorted in exactly same order than shapekeys
This commit is contained in:
Julien Duroure 2019-08-23 07:29:41 +02:00
parent 3c3c2243db
commit fc320ea236
5 changed files with 111 additions and 77 deletions

View File

@ -15,7 +15,7 @@
bl_info = {
'name': 'glTF 2.0 format',
'author': 'Julien Duroure, Norbert Nopper, Urs Hanselmann, Moritz Becher, Benjamin Schmithüsen, Jim Eckerlein, and many external contributors',
"version": (0, 9, 48),
"version": (0, 9, 49),
'blender': (2, 80, 0),
'location': 'File > Import-Export',
'description': 'Import-Export as glTF 2.0',

View File

@ -499,14 +499,14 @@ def extract_primitives(glTF, blender_mesh, blender_vertex_groups, modifiers, exp
blender_shape_keys = []
if blender_mesh.shape_keys is not None:
morph_max = len(blender_mesh.shape_keys.key_blocks) - 1
for blender_shape_key in blender_mesh.shape_keys.key_blocks:
if blender_shape_key != blender_shape_key.relative_key:
blender_shape_keys.append(ShapeKey(
blender_shape_key,
blender_shape_key.normals_vertex_get(), # calculate vertex normals for this shape key
blender_shape_key.normals_polygon_get())) # calculate polygon normals for this shape key
if blender_shape_key.mute is False:
morph_max += 1
blender_shape_keys.append(ShapeKey(
blender_shape_key,
blender_shape_key.normals_vertex_get(), # calculate vertex normals for this shape key
blender_shape_key.normals_polygon_get())) # calculate polygon normals for this shape key
#
# Convert polygon to primitive indices and eliminate invalid ones. Assign to material.

View File

@ -64,12 +64,35 @@ def gather_animation_channels(blender_action: bpy.types.Action,
channels.append(channel)
else:
for channel_group in __get_channel_groups(blender_action, blender_object, export_settings):
channel = __gather_animation_channel(channel_group, blender_object, export_settings, None, None, None, None, blender_action.name)
channel_group_sorted = __get_channel_group_sorted(channel_group, blender_object)
channel = __gather_animation_channel(channel_group_sorted, blender_object, export_settings, None, None, None, None, blender_action.name)
if channel is not None:
channels.append(channel)
return channels
def __get_channel_group_sorted(channels: typing.Tuple[bpy.types.FCurve], blender_object: bpy.types.Object):
# if this is shapekey animation, we need to sort in same order than shapekeys
# else, no need to sort
if blender_object.type == "MESH":
first_channel = channels[0]
object_path = get_target_object_path(first_channel.data_path)
if object_path:
# This is shapekeys, we need to sort channels
shapekeys_idx = {}
cpt_sk = 0
for sk in blender_object.data.shape_keys.key_blocks:
if sk == sk.relative_key:
continue
if sk.mute is True:
continue
shapekeys_idx[sk.name] = cpt_sk
cpt_sk += 1
return tuple(sorted(channels, key=lambda x: shapekeys_idx[blender_object.data.shape_keys.path_resolve(get_target_object_path(x.data_path)).name]))
# if not shapekeys, stay in same order, because order doesn't matter
return channels
def __gather_animation_channel(channels: typing.Tuple[bpy.types.FCurve],
blender_object: bpy.types.Object,
@ -164,12 +187,17 @@ def __get_channel_groups(blender_action: bpy.types.Action, blender_object: bpy.t
else:
try:
target = gltf2_blender_get.get_object_from_datapath(blender_object, object_path)
if blender_object.type == "MESH":
shape_key = blender_object.data.shape_keys.path_resolve(object_path)
if shape_key.mute is True:
continue
except ValueError as e:
# if the object is a mesh and the action target path can not be resolved, we know that this is a morph
# animation.
if blender_object.type == "MESH":
# if you need the specific shape key for some reason, this is it:
# shape_key = blender_object.data.shape_keys.path_resolve(object_path)
shape_key = blender_object.data.shape_keys.path_resolve(object_path)
if shape_key.mute is True:
continue
target = blender_object.data.shape_keys
else:
gltf2_io_debug.print_console("WARNING", "Animation target {} not found".format(object_path))

View File

@ -83,7 +83,8 @@ def __gather_extras(blender_mesh: bpy.types.Mesh,
target_names = []
for blender_shape_key in blender_mesh.shape_keys.key_blocks:
if blender_shape_key != blender_shape_key.relative_key:
target_names.append(blender_shape_key.name)
if blender_shape_key.mute is False:
target_names.append(blender_shape_key.name)
extras['targetNames'] = target_names
if extras:
@ -130,7 +131,8 @@ def __gather_weights(blender_mesh: bpy.types.Mesh,
for blender_shape_key in blender_mesh.shape_keys.key_blocks:
if blender_shape_key != blender_shape_key.relative_key:
weights.append(blender_shape_key.value)
if blender_shape_key.mute is False:
weights.append(blender_shape_key.value)
return weights

View File

@ -143,87 +143,91 @@ def __gather_targets(blender_primitive, blender_mesh, modifiers, export_settings
if blender_mesh.shape_keys is not None:
morph_index = 0
for blender_shape_key in blender_mesh.shape_keys.key_blocks:
if blender_shape_key != blender_shape_key.relative_key:
if blender_shape_key == blender_shape_key.relative_key:
continue
target_position_id = 'MORPH_POSITION_' + str(morph_index)
target_normal_id = 'MORPH_NORMAL_' + str(morph_index)
target_tangent_id = 'MORPH_TANGENT_' + str(morph_index)
if blender_shape_key.mute is True:
continue
if blender_primitive["attributes"].get(target_position_id):
target = {}
internal_target_position = blender_primitive["attributes"][target_position_id]
target_position_id = 'MORPH_POSITION_' + str(morph_index)
target_normal_id = 'MORPH_NORMAL_' + str(morph_index)
target_tangent_id = 'MORPH_TANGENT_' + str(morph_index)
if blender_primitive["attributes"].get(target_position_id):
target = {}
internal_target_position = blender_primitive["attributes"][target_position_id]
binary_data = gltf2_io_binary_data.BinaryData.from_list(
internal_target_position,
gltf2_io_constants.ComponentType.Float
)
target["POSITION"] = gltf2_io.Accessor(
buffer_view=binary_data,
byte_offset=None,
component_type=gltf2_io_constants.ComponentType.Float,
count=len(internal_target_position) // gltf2_io_constants.DataType.num_elements(
gltf2_io_constants.DataType.Vec3),
extensions=None,
extras=None,
max=gltf2_blender_utils.max_components(
internal_target_position, gltf2_io_constants.DataType.Vec3),
min=gltf2_blender_utils.min_components(
internal_target_position, gltf2_io_constants.DataType.Vec3),
name=None,
normalized=None,
sparse=None,
type=gltf2_io_constants.DataType.Vec3
)
if export_settings[NORMALS] \
and export_settings[MORPH_NORMAL] \
and blender_primitive["attributes"].get(target_normal_id):
internal_target_normal = blender_primitive["attributes"][target_normal_id]
binary_data = gltf2_io_binary_data.BinaryData.from_list(
internal_target_position,
gltf2_io_constants.ComponentType.Float
internal_target_normal,
gltf2_io_constants.ComponentType.Float,
)
target["POSITION"] = gltf2_io.Accessor(
target['NORMAL'] = gltf2_io.Accessor(
buffer_view=binary_data,
byte_offset=None,
component_type=gltf2_io_constants.ComponentType.Float,
count=len(internal_target_position) // gltf2_io_constants.DataType.num_elements(
count=len(internal_target_normal) // gltf2_io_constants.DataType.num_elements(
gltf2_io_constants.DataType.Vec3),
extensions=None,
extras=None,
max=gltf2_blender_utils.max_components(
internal_target_position, gltf2_io_constants.DataType.Vec3),
min=gltf2_blender_utils.min_components(
internal_target_position, gltf2_io_constants.DataType.Vec3),
max=None,
min=None,
name=None,
normalized=None,
sparse=None,
type=gltf2_io_constants.DataType.Vec3
)
if export_settings[NORMALS] \
and export_settings[MORPH_NORMAL] \
and blender_primitive["attributes"].get(target_normal_id):
internal_target_normal = blender_primitive["attributes"][target_normal_id]
binary_data = gltf2_io_binary_data.BinaryData.from_list(
internal_target_normal,
gltf2_io_constants.ComponentType.Float,
)
target['NORMAL'] = gltf2_io.Accessor(
buffer_view=binary_data,
byte_offset=None,
component_type=gltf2_io_constants.ComponentType.Float,
count=len(internal_target_normal) // gltf2_io_constants.DataType.num_elements(
gltf2_io_constants.DataType.Vec3),
extensions=None,
extras=None,
max=None,
min=None,
name=None,
normalized=None,
sparse=None,
type=gltf2_io_constants.DataType.Vec3
)
if export_settings[TANGENTS] \
and export_settings[MORPH_TANGENT] \
and blender_primitive["attributes"].get(target_tangent_id):
internal_target_tangent = blender_primitive["attributes"][target_tangent_id]
binary_data = gltf2_io_binary_data.BinaryData.from_list(
internal_target_tangent,
gltf2_io_constants.ComponentType.Float,
)
target['TANGENT'] = gltf2_io.Accessor(
buffer_view=binary_data,
byte_offset=None,
component_type=gltf2_io_constants.ComponentType.Float,
count=len(internal_target_tangent) // gltf2_io_constants.DataType.num_elements(
gltf2_io_constants.DataType.Vec3),
extensions=None,
extras=None,
max=None,
min=None,
name=None,
normalized=None,
sparse=None,
type=gltf2_io_constants.DataType.Vec3
)
targets.append(target)
morph_index += 1
if export_settings[TANGENTS] \
and export_settings[MORPH_TANGENT] \
and blender_primitive["attributes"].get(target_tangent_id):
internal_target_tangent = blender_primitive["attributes"][target_tangent_id]
binary_data = gltf2_io_binary_data.BinaryData.from_list(
internal_target_tangent,
gltf2_io_constants.ComponentType.Float,
)
target['TANGENT'] = gltf2_io.Accessor(
buffer_view=binary_data,
byte_offset=None,
component_type=gltf2_io_constants.ComponentType.Float,
count=len(internal_target_tangent) // gltf2_io_constants.DataType.num_elements(
gltf2_io_constants.DataType.Vec3),
extensions=None,
extras=None,
max=None,
min=None,
name=None,
normalized=None,
sparse=None,
type=gltf2_io_constants.DataType.Vec3
)
targets.append(target)
morph_index += 1
return targets
return None