def gather_joint(blender_bone, export_settings): """ Generate a glTF2 node from a blender bone, as joints in glTF2 are simply nodes :param blender_bone: a blender PoseBone :param export_settings: the settings for this export :return: a glTF2 node (acting as a joint) """ axis_basis_change = mathutils.Matrix.Identity(4) if export_settings['gltf_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))) # extract bone transform if blender_bone.parent is None: correction_matrix_local = gltf2_blender_math.multiply( axis_basis_change, blender_bone.bone.matrix_local) else: correction_matrix_local = gltf2_blender_math.multiply( blender_bone.parent.bone.matrix_local.inverted(), blender_bone.bone.matrix_local) matrix_basis = blender_bone.matrix_basis if export_settings['gltf_bake_skins']: gltf2_io_debug.print_console("WARNING", "glTF bake skins not supported") # matrix_basis = blender_object.convert_space(blender_bone, blender_bone.matrix, from_space='POSE', # to_space='LOCAL') trans, rot, sca = gltf2_blender_extract.decompose_transition( gltf2_blender_math.multiply(correction_matrix_local, matrix_basis), 'JOINT', export_settings) translation, rotation, scale = (None, None, None) if trans[0] != 0.0 or trans[1] != 0.0 or trans[2] != 0.0: translation = [trans[0], trans[1], trans[2]] if rot[0] != 0.0 or rot[1] != 0.0 or rot[2] != 0.0 or rot[3] != 1.0: rotation = [rot[0], rot[1], rot[2], rot[3]] if sca[0] != 1.0 or sca[1] != 1.0 or sca[2] != 1.0: scale = [sca[0], sca[1], sca[2]] # traverse into children children = [] for bone in blender_bone.children: children.append(gather_joint(bone, export_settings)) # finally add to the joints array containing all the joints in the hierarchy return gltf2_io.Node(camera=None, children=children, extensions=None, extras=None, matrix=None, mesh=None, name=blender_bone.name, rotation=rotation, scale=scale, skin=None, translation=translation, weights=None)
def __collect_matrices(bone): inverse_bind_matrix = gltf2_blender_math.multiply( axis_basis_change, gltf2_blender_math.multiply(blender_object.matrix_world, bone.bone.matrix_local)).inverted() matrices.append(inverse_bind_matrix) for child in bone.children: __collect_matrices(child)
def gather_joint(blender_object, blender_bone, export_settings): """ Generate a glTF2 node from a blender bone, as joints in glTF2 are simply nodes. :param blender_bone: a blender PoseBone :param export_settings: the settings for this export :return: a glTF2 node (acting as a joint) """ 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))) # extract bone transform if blender_bone.parent is None: correction_matrix_local = gltf2_blender_math.multiply(axis_basis_change, blender_bone.bone.matrix_local) else: correction_matrix_local = gltf2_blender_math.multiply( blender_bone.parent.bone.matrix_local.inverted(), blender_bone.bone.matrix_local) matrix_basis = blender_bone.matrix_basis trans, rot, sca = gltf2_blender_extract.decompose_transition( gltf2_blender_math.multiply(correction_matrix_local, matrix_basis), export_settings) translation, rotation, scale = (None, None, None) if trans[0] != 0.0 or trans[1] != 0.0 or trans[2] != 0.0: translation = [trans[0], trans[1], trans[2]] if rot[0] != 1.0 or rot[1] != 0.0 or rot[2] != 0.0 or rot[3] != 0.0: rotation = [rot[1], rot[2], rot[3], rot[0]] if sca[0] != 1.0 or sca[1] != 1.0 or sca[2] != 1.0: scale = [sca[0], sca[1], sca[2]] # traverse into children children = [] if export_settings["gltf_def_bones"] is False: for bone in blender_bone.children: children.append(gather_joint(blender_object, bone, export_settings)) else: _, children_, _ = gltf2_blender_gather_skins.get_bone_tree(None, blender_bone.id_data) if blender_bone.name in children_.keys(): for bone in children_[blender_bone.name]: children.append(gather_joint(blender_object, blender_bone.id_data.pose.bones[bone], export_settings)) # finally add to the joints array containing all the joints in the hierarchy return gltf2_io.Node( camera=None, children=children, extensions=None, extras=__gather_extras(blender_bone, export_settings), matrix=None, mesh=None, name=blender_bone.name, rotation=rotation, scale=scale, skin=None, translation=translation, weights=None )
def gather_joint(blender_bone, export_settings): """ Generate a glTF2 node from a blender bone, as joints in glTF2 are simply nodes. :param blender_bone: a blender PoseBone :param export_settings: the settings for this export :return: a glTF2 node (acting as a joint) """ 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))) # extract bone transform if blender_bone.parent is None: correction_matrix_local = gltf2_blender_math.multiply(axis_basis_change, blender_bone.bone.matrix_local) else: correction_matrix_local = gltf2_blender_math.multiply( blender_bone.parent.bone.matrix_local.inverted(), blender_bone.bone.matrix_local) matrix_basis = blender_bone.matrix_basis if export_settings[gltf2_blender_export_keys.BAKE_SKINS]: gltf2_io_debug.print_console("WARNING", "glTF bake skins not supported") # matrix_basis = blender_object.convert_space(blender_bone, blender_bone.matrix, from_space='POSE', # to_space='LOCAL') trans, rot, sca = gltf2_blender_extract.decompose_transition( gltf2_blender_math.multiply(correction_matrix_local, matrix_basis), export_settings) translation, rotation, scale = (None, None, None) if trans[0] != 0.0 or trans[1] != 0.0 or trans[2] != 0.0: translation = [trans[0], trans[1], trans[2]] if rot[0] != 0.0 or rot[1] != 0.0 or rot[2] != 0.0 or rot[3] != 1.0: rotation = [rot[0], rot[1], rot[2], rot[3]] if sca[0] != 1.0 or sca[1] != 1.0 or sca[2] != 1.0: scale = [sca[0], sca[1], sca[2]] # traverse into children children = [] for bone in blender_bone.children: children.append(gather_joint(bone, export_settings)) # finally add to the joints array containing all the joints in the hierarchy return gltf2_io.Node( camera=None, children=children, extensions=None, extras=None, matrix=None, mesh=None, name=blender_bone.name, rotation=rotation, scale=scale, skin=None, translation=translation, weights=None )
def __collect_matrices(bone): inverse_bind_matrix = gltf2_blender_math.multiply( axis_basis_change, gltf2_blender_math.multiply(blender_object.matrix_world, bone.bone.matrix_local)).inverted() matrices.append(inverse_bind_matrix) if export_settings['gltf_def_bones'] is False: for child in bone.children: __collect_matrices(child) else: if bone.name in children_.keys(): for child in children_[bone.name]: __collect_matrices(blender_object.pose.bones[child])
def __gather_inverse_bind_matrices(blender_object, export_settings): inverse_matrices = [] 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))) # # artificial torso, as needed by glTF # inverse_bind_matrix = blender_object.matrix_world.inverted() * axis_basis_change.inverted() # for column in range(0, 4): # for row in range(0, 4): # inverse_matrices.append(inverse_bind_matrix[row][column]) # for blender_bone in blender_object.pose.bones: inverse_bind_matrix = gltf2_blender_math.multiply( axis_basis_change, blender_bone.bone.matrix_local) bind_shape_matrix = gltf2_blender_math.multiply( gltf2_blender_math.multiply( axis_basis_change, blender_object.matrix_world.inverted()), axis_basis_change.inverted()) inverse_bind_matrix = gltf2_blender_math.multiply( inverse_bind_matrix.inverted(), bind_shape_matrix) for column in range(0, 4): for row in range(0, 4): inverse_matrices.append(inverse_bind_matrix[row][column]) binary_data = gltf2_io_binary_data.BinaryData.from_list( inverse_matrices, gltf2_io_constants.ComponentType.Float) return gltf2_io.Accessor( buffer_view=binary_data, byte_offset=None, component_type=gltf2_io_constants.ComponentType.Float, count=len(inverse_matrices) // gltf2_io_constants.DataType.num_elements( gltf2_io_constants.DataType.Mat4), extensions=None, extras=None, max=None, min=None, name=None, normalized=None, sparse=None, type=gltf2_io_constants.DataType.Mat4)
def __gather_inverse_bind_matrices(blender_object, mesh_object, export_settings): inverse_matrices = [] 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))) # # artificial torso, as needed by glTF # inverse_bind_matrix = blender_object.matrix_world.inverted() * axis_basis_change.inverted() # for column in range(0, 4): # for row in range(0, 4): # inverse_matrices.append(inverse_bind_matrix[row][column]) # for blender_bone in blender_object.pose.bones: matrix_world = gltf2_blender_math.multiply(blender_object.matrix_world, mesh_object.matrix_world.inverted()) inverse_bind_matrix = gltf2_blender_math.multiply( axis_basis_change, gltf2_blender_math.multiply( matrix_world, blender_bone.bone.matrix_local ) ).inverted() for column in range(0, 4): for row in range(0, 4): inverse_matrices.append(inverse_bind_matrix[row][column]) binary_data = gltf2_io_binary_data.BinaryData.from_list(inverse_matrices, gltf2_io_constants.ComponentType.Float) return gltf2_blender_gather_accessors.gather_accessor( binary_data, gltf2_io_constants.ComponentType.Float, len(inverse_matrices) // gltf2_io_constants.DataType.num_elements(gltf2_io_constants.DataType.Mat4), None, None, gltf2_io_constants.DataType.Mat4, export_settings )
def __gather_children(blender_object, blender_scene, export_settings): children = [] # standard children for _child_object in blender_object.children: if _child_object.parent_bone: # this is handled further down, # as the object should be a child of the specific bone, # not the Armature object continue child_object = _child_object.proxy if _child_object.proxy else _child_object node = gather_node( child_object, child_object.library.name if child_object.library else None, blender_scene, None, export_settings) if node is not None: children.append(node) # blender dupli objects if blender_object.instance_type == 'COLLECTION' and blender_object.instance_collection: for dupli_object in blender_object.instance_collection.objects: if dupli_object.parent is not None: continue if dupli_object.type == "ARMATURE": continue # There is probably a proxy node = gather_node( dupli_object, dupli_object.library.name if dupli_object.library else None, blender_scene, blender_object.name, export_settings) if node is not None: children.append(node) # blender bones if blender_object.type == "ARMATURE": root_joints = [] if export_settings["gltf_def_bones"] is False: bones = blender_object.pose.bones else: bones, _, _ = gltf2_blender_gather_skins.get_bone_tree( None, blender_object) bones = [blender_object.pose.bones[b.name] for b in bones] for blender_bone in bones: if not blender_bone.parent: joint = gltf2_blender_gather_joints.gather_joint( blender_object, blender_bone, export_settings) children.append(joint) root_joints.append(joint) # handle objects directly parented to bones direct_bone_children = [ child for child in blender_object.children if child.parent_bone ] def find_parent_joint(joints, name): for joint in joints: if joint.name == name: return joint parent_joint = find_parent_joint(joint.children, name) if parent_joint: return parent_joint return None for child in direct_bone_children: # find parent joint parent_joint = find_parent_joint(root_joints, child.parent_bone) if not parent_joint: continue child_node = gather_node(child, None, None, None, export_settings) if child_node is None: continue blender_bone = blender_object.pose.bones[parent_joint.name] # fix rotation if export_settings[gltf2_blender_export_keys.YUP]: rot = child_node.rotation if rot is None: rot = [0, 0, 0, 1] rot_quat = Quaternion(rot) axis_basis_change = 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))) mat = gltf2_blender_math.multiply(child.matrix_parent_inverse, child.matrix_basis) mat = gltf2_blender_math.multiply(mat, axis_basis_change) _, rot_quat, _ = mat.decompose() child_node.rotation = [ rot_quat[1], rot_quat[2], rot_quat[3], rot_quat[0] ] # fix translation (in blender bone's tail is the origin for children) trans, _, _ = child.matrix_local.decompose() if trans is None: trans = [0, 0, 0] # bones go down their local y axis if blender_bone.matrix.to_scale()[1] >= 1e-6: bone_tail = [ 0, blender_bone.length / blender_bone.matrix.to_scale()[1], 0 ] else: bone_tail = [0, 0, 0] # If scale is 0, tail == head child_node.translation = [ trans[idx] + bone_tail[idx] for idx in range(3) ] parent_joint.children.append(child_node) return children
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 )
def __gather_children(blender_object, blender_scene, export_settings): children = [] # standard children for child_object in blender_object.children: if child_object.parent_bone: # this is handled further down, # as the object should be a child of the specific bone, # not the Armature object continue node = gather_node(child_object, blender_scene, export_settings) if node is not None: children.append(node) # blender dupli objects if bpy.app.version < (2, 80, 0): if blender_object.dupli_type == 'GROUP' and blender_object.dupli_group: for dupli_object in blender_object.dupli_group.objects: node = gather_node(dupli_object, blender_scene, export_settings) if node is not None: children.append(node) else: if blender_object.instance_type == 'COLLECTION' and blender_object.instance_collection: for dupli_object in blender_object.instance_collection.objects: node = gather_node(dupli_object, blender_scene, export_settings) if node is not None: children.append(node) # blender bones if blender_object.type == "ARMATURE": root_joints = [] for blender_bone in blender_object.pose.bones: if not blender_bone.parent: joint = gltf2_blender_gather_joints.gather_joint( blender_bone, export_settings) children.append(joint) root_joints.append(joint) # handle objects directly parented to bones direct_bone_children = [ child for child in blender_object.children if child.parent_bone ] def find_parent_joint(joints, name): for joint in joints: if joint.name == name: return joint parent_joint = find_parent_joint(joint.children, name) if parent_joint: return parent_joint return None for child in direct_bone_children: # find parent joint parent_joint = find_parent_joint(root_joints, child.parent_bone) if not parent_joint: continue child_node = gather_node(child, None, export_settings) if child_node is None: continue blender_bone = blender_object.pose.bones[parent_joint.name] # fix rotation if export_settings[gltf2_blender_export_keys.YUP]: rot = child_node.rotation if rot is None: rot = [0, 0, 0, 1] rot_quat = Quaternion(rot) axis_basis_change = 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))) mat = gltf2_blender_math.multiply(axis_basis_change, child.matrix_basis) mat = gltf2_blender_math.multiply(child.matrix_parent_inverse, mat) _, rot_quat, _ = mat.decompose() child_node.rotation = [ rot_quat[1], rot_quat[2], rot_quat[3], rot_quat[0] ] # fix translation (in blender bone's tail is the origin for children) trans, _, _ = child.matrix_local.decompose() if trans is None: trans = [0, 0, 0] # bones go down their local y axis bone_tail = [0, blender_bone.length, 0] child_node.translation = [ trans[idx] + bone_tail[idx] for idx in range(3) ] parent_joint.children.append(child_node) return children
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 )