def list_to_mathutils( values: typing.List[float], data_path: str) -> typing.Union[Vector, Quaternion, Euler]: """Transform a list to blender py object.""" target = get_target_property_name(data_path) if target == 'delta_location': return Vector( values) # TODO Should be Vector(values) - Vector(something)? elif target == 'delta_rotation_euler': return Euler(values).to_quaternion( ) # TODO Should be Euler(values).to_quaternion() @ something? elif target == 'location': return Vector(values) elif target == 'rotation_axis_angle': angle = values[0] axis = values[1:] return Quaternion(axis, math.radians(angle)) elif target == 'rotation_euler': return Euler(values).to_quaternion() elif target == 'rotation_quaternion': return Quaternion(values) elif target == 'scale': return Vector(values) elif target == 'value': return Vector(values) return values
def swizzle_yup(v: typing.Union[Vector, Quaternion], data_path: str) -> typing.Union[Vector, Quaternion]: """Manage Yup.""" target = get_target_property_name(data_path) swizzle_func = { "location": swizzle_yup_location, "rotation_axis_angle": swizzle_yup_rotation, "rotation_euler": swizzle_yup_rotation, "rotation_quaternion": swizzle_yup_rotation, "scale": swizzle_yup_scale, "value": swizzle_yup_value }.get(target) if swizzle_func is None: raise RuntimeError("Cannot transform values at {}".format(data_path)) return swizzle_func(v)
def transform(v: typing.Union[Vector, Quaternion], data_path: str, transform: Matrix = Matrix.Identity(4)) -> typing \ .Union[Vector, Quaternion]: """Manage transformations.""" target = get_target_property_name(data_path) transform_func = { "location": transform_location, "rotation_axis_angle": transform_rotation, "rotation_euler": transform_rotation, "rotation_quaternion": transform_rotation, "scale": transform_scale, "value": transform_value }.get(target) if transform_func is None: raise RuntimeError("Cannot transform values at {}".format(data_path)) return transform_func(v, transform)
def swizzle_yup(v: typing.Union[Vector, Quaternion], data_path: str) -> typing.Union[Vector, Quaternion]: """Manage Yup.""" target = get_target_property_name(data_path) swizzle_func = { "delta_location": swizzle_yup_location, "delta_rotation_euler": swizzle_yup_rotation, "location": swizzle_yup_location, "rotation_axis_angle": swizzle_yup_rotation, "rotation_euler": swizzle_yup_rotation, "rotation_quaternion": swizzle_yup_rotation, "scale": swizzle_yup_scale, "value": swizzle_yup_value }.get(target) if swizzle_func is None: raise RuntimeError("Cannot transform values at {}".format(data_path)) return swizzle_func(v)
def transform(v: typing.Union[Vector, Quaternion], data_path: str, transform: Matrix = Matrix.Identity(4)) -> typing \ .Union[Vector, Quaternion]: """Manage transformations.""" target = get_target_property_name(data_path) transform_func = { "delta_location": transform_location, "delta_rotation_euler": transform_rotation, "location": transform_location, "rotation_axis_angle": transform_rotation, "rotation_euler": transform_rotation, "rotation_quaternion": transform_rotation, "scale": transform_scale, "value": transform_value }.get(target) if transform_func is None: raise RuntimeError("Cannot transform values at {}".format(data_path)) return transform_func(v, transform)
def list_to_mathutils(values: typing.List[float], data_path: str) -> typing.Union[Vector, Quaternion, Euler]: """Transform a list to blender py object.""" target = get_target_property_name(data_path) if target == 'delta_location': return Vector(values) # TODO Should be Vector(values) - Vector(something)? elif target == 'delta_rotation_euler': return Euler(values).to_quaternion() # TODO Should be multiply(Euler(values).to_quaternion(), something)? elif target == 'location': return Vector(values) elif target == 'rotation_axis_angle': angle = values[0] axis = values[1:] return Quaternion(axis, math.radians(angle)) elif target == 'rotation_euler': return Euler(values).to_quaternion() elif target == 'rotation_quaternion': return Quaternion(values) elif target == 'scale': return Vector(values) elif target == 'value': return Vector(values) return values
def __gather_output(channels: typing.Tuple[bpy.types.FCurve], parent_inverse, blender_object_if_armature: typing.Optional[bpy.types.Object], non_keyed_values: typing.Tuple[typing.Optional[float]], bake_bone: typing.Union[str, None], bake_channel: typing.Union[str, None], bake_range_start, bake_range_end, action_name, driver_obj, export_settings ) -> gltf2_io.Accessor: """Gather the data of the keyframes.""" keyframes = gltf2_blender_gather_animation_sampler_keyframes.gather_keyframes(blender_object_if_armature, channels, non_keyed_values, bake_bone, bake_channel, 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 else: target_datapath = [c for c in channels if c is not None][0].data_path is_yup = export_settings[gltf2_blender_export_keys.YUP] # bone animations need to be handled differently as they are in a different coordinate system if bake_bone is None: object_path = get_target_object_path(target_datapath) else: object_path = None is_armature_animation = bake_bone is not None or (blender_object_if_armature is not None and object_path != "") if is_armature_animation: if bake_bone is None: bone = gltf2_blender_get.get_object_from_datapath(blender_object_if_armature, object_path) else: bone = blender_object_if_armature.pose.bones[bake_bone] if isinstance(bone, bpy.types.PoseBone): if bone.parent is None: axis_basis_change = mathutils.Matrix.Identity(4) if export_settings[gltf2_blender_export_keys.YUP]: axis_basis_change = mathutils.Matrix( ((1.0, 0.0, 0.0, 0.0), (0.0, 0.0, 1.0, 0.0), (0.0, -1.0, 0.0, 0.0), (0.0, 0.0, 0.0, 1.0))) correction_matrix_local = axis_basis_change @ bone.bone.matrix_local else: correction_matrix_local = ( bone.parent.bone.matrix_local.inverted() @ bone.bone.matrix_local ) transform = correction_matrix_local else: transform = mathutils.Matrix.Identity(4) else: transform = parent_inverse values = [] for keyframe in keyframes: # Transform the data and build gltf control points value = gltf2_blender_math.transform(keyframe.value, target_datapath, transform) if is_yup and not is_armature_animation: value = gltf2_blender_math.swizzle_yup(value, target_datapath) keyframe_value = gltf2_blender_math.mathutils_to_gltf(value) if keyframe.in_tangent is not None: # we can directly transform the tangent as it currently is represented by a control point in_tangent = gltf2_blender_math.transform(keyframe.in_tangent, target_datapath, transform) if is_yup and blender_object_if_armature is None: in_tangent = gltf2_blender_math.swizzle_yup(in_tangent, target_datapath) # the tangent in glTF is relative to the keyframe value if not isinstance(value, list): in_tangent = value - in_tangent else: in_tangent = [value[i] - in_tangent[i] for i in range(len(value))] keyframe_value = gltf2_blender_math.mathutils_to_gltf(in_tangent) + keyframe_value # append if keyframe.out_tangent is not None: # we can directly transform the tangent as it currently is represented by a control point out_tangent = gltf2_blender_math.transform(keyframe.out_tangent, target_datapath, transform) if is_yup and blender_object_if_armature is None: out_tangent = gltf2_blender_math.swizzle_yup(out_tangent, target_datapath) # the tangent in glTF is relative to the keyframe value if not isinstance(value, list): out_tangent = value - out_tangent else: out_tangent = [value[i] - out_tangent[i] for i in range(len(value))] keyframe_value = keyframe_value + gltf2_blender_math.mathutils_to_gltf(out_tangent) # append values += keyframe_value # store the keyframe data in a binary buffer component_type = gltf2_io_constants.ComponentType.Float if get_target_property_name(target_datapath) == "value": # channels with 'weight' targets must have scalar accessors data_type = gltf2_io_constants.DataType.Scalar else: data_type = gltf2_io_constants.DataType.vec_type_from_num(len(keyframes[0].value)) return gltf2_io.Accessor( buffer_view=gltf2_io_binary_data.BinaryData.from_list(values, component_type), byte_offset=None, component_type=component_type, count=len(values) // gltf2_io_constants.DataType.num_elements(data_type), extensions=None, extras=None, max=None, min=None, name=None, normalized=None, sparse=None, type=data_type )
def __gather_output(channels: typing.Tuple[bpy.types.FCurve], parent_inverse, blender_object_if_armature: typing.Optional[ bpy.types.Object], export_settings) -> gltf2_io.Accessor: """Gather the data of the keyframes.""" keyframes = gltf2_blender_gather_animation_sampler_keyframes.gather_keyframes( channels, export_settings) target_datapath = channels[0].data_path transform = parent_inverse is_yup = export_settings[gltf2_blender_export_keys.YUP] object_path = get_target_object_path(target_datapath) is_armature_animation = blender_object_if_armature is not None and object_path != "" if is_armature_animation: bone = blender_object_if_armature.path_resolve(object_path) if isinstance(bone, bpy.types.PoseBone): if bone.parent is not None: parent_transform = bone.parent.bone.matrix_local transform = gltf2_blender_math.multiply( transform, parent_transform.inverted()) # if not is_yup: # transform = gltf2_blender_math.multiply(transform, gltf2_blender_math.to_zup()) else: # only apply the y-up conversion to root bones, as child bones already are in the y-up space if is_yup: transform = gltf2_blender_math.multiply( transform, gltf2_blender_math.to_yup()) local_transform = bone.bone.matrix_local transform = gltf2_blender_math.multiply(transform, local_transform) values = [] for keyframe in keyframes: # Transform the data and extract value = gltf2_blender_math.transform(keyframe.value, target_datapath, transform) if is_yup and not is_armature_animation: value = gltf2_blender_math.swizzle_yup(value, target_datapath) keyframe_value = gltf2_blender_math.mathutils_to_gltf(value) if keyframe.in_tangent is not None: in_tangent = gltf2_blender_math.transform(keyframe.in_tangent, target_datapath, transform) if is_yup and blender_object_if_armature is None: in_tangent = gltf2_blender_math.swizzle_yup( in_tangent, target_datapath) keyframe_value = gltf2_blender_math.mathutils_to_gltf( in_tangent) + keyframe_value if keyframe.out_tangent is not None: out_tangent = gltf2_blender_math.transform(keyframe.out_tangent, target_datapath, transform) if is_yup and blender_object_if_armature is None: out_tangent = gltf2_blender_math.swizzle_yup( out_tangent, target_datapath) keyframe_value = keyframe_value + gltf2_blender_math.mathutils_to_gltf( out_tangent) values += keyframe_value component_type = gltf2_io_constants.ComponentType.Float if get_target_property_name(target_datapath) == "value": # channels with 'weight' targets must have scalar accessors data_type = gltf2_io_constants.DataType.Scalar else: data_type = gltf2_io_constants.DataType.vec_type_from_num( len(keyframes[0].value)) return gltf2_io.Accessor( buffer_view=gltf2_io_binary_data.BinaryData.from_list( values, component_type), byte_offset=None, component_type=component_type, count=len(values) // gltf2_io_constants.DataType.num_elements(data_type), extensions=None, extras=None, max=None, min=None, name=None, normalized=None, sparse=None, type=data_type)
def __gather_output(channels: typing.Tuple[bpy.types.FCurve], parent_inverse, blender_object_if_armature: typing.Optional[bpy.types.Object], export_settings ) -> gltf2_io.Accessor: """Gather the data of the keyframes.""" keyframes = gltf2_blender_gather_animation_sampler_keyframes.gather_keyframes(blender_object_if_armature, channels, export_settings) target_datapath = channels[0].data_path transform = mathutils.Matrix.Identity(4) is_yup = export_settings[gltf2_blender_export_keys.YUP] # bone animations need to be handled differently as they are in a different coordinate system object_path = get_target_object_path(target_datapath) is_armature_animation = blender_object_if_armature is not None and object_path != "" if is_armature_animation: bone = gltf2_blender_get.get_object_from_datapath(blender_object_if_armature, object_path) if isinstance(bone, bpy.types.PoseBone): if bone.parent is None: axis_basis_change = mathutils.Matrix.Identity(4) if export_settings[gltf2_blender_export_keys.YUP]: axis_basis_change = mathutils.Matrix( ((1.0, 0.0, 0.0, 0.0), (0.0, 0.0, 1.0, 0.0), (0.0, -1.0, 0.0, 0.0), (0.0, 0.0, 0.0, 1.0))) correction_matrix_local = gltf2_blender_math.multiply(axis_basis_change, bone.bone.matrix_local) else: correction_matrix_local = gltf2_blender_math.multiply( bone.parent.bone.matrix_local.inverted(), bone.bone.matrix_local) transform = correction_matrix_local values = [] for keyframe in keyframes: # Transform the data and build gltf control points value = gltf2_blender_math.transform(keyframe.value, target_datapath, transform) if is_yup and not is_armature_animation: value = gltf2_blender_math.swizzle_yup(value, target_datapath) keyframe_value = gltf2_blender_math.mathutils_to_gltf(value) if keyframe.in_tangent is not None: # we can directly transform the tangent as it currently is represented by a control point in_tangent = gltf2_blender_math.transform(keyframe.in_tangent, target_datapath, transform) if is_yup and blender_object_if_armature is None: in_tangent = gltf2_blender_math.swizzle_yup(in_tangent, target_datapath) # the tangent in glTF is relative to the keyframe value in_tangent = value - in_tangent if not isinstance(value, list) else [value[0] - in_tangent[0]] keyframe_value = gltf2_blender_math.mathutils_to_gltf(in_tangent) + keyframe_value # append if keyframe.out_tangent is not None: # we can directly transform the tangent as it currently is represented by a control point out_tangent = gltf2_blender_math.transform(keyframe.out_tangent, target_datapath, transform) if is_yup and blender_object_if_armature is None: out_tangent = gltf2_blender_math.swizzle_yup(out_tangent, target_datapath) # the tangent in glTF is relative to the keyframe value out_tangent = value - out_tangent if not isinstance(value, list) else [value[0] - out_tangent[0]] keyframe_value = keyframe_value + gltf2_blender_math.mathutils_to_gltf(out_tangent) # append values += keyframe_value # store the keyframe data in a binary buffer component_type = gltf2_io_constants.ComponentType.Float if get_target_property_name(target_datapath) == "value": # channels with 'weight' targets must have scalar accessors data_type = gltf2_io_constants.DataType.Scalar else: data_type = gltf2_io_constants.DataType.vec_type_from_num(len(keyframes[0].value)) return gltf2_io.Accessor( buffer_view=gltf2_io_binary_data.BinaryData.from_list(values, component_type), byte_offset=None, component_type=component_type, count=len(values) // gltf2_io_constants.DataType.num_elements(data_type), extensions=None, extras=None, max=None, min=None, name=None, normalized=None, sparse=None, type=data_type )