def __gather_indices(blender_primitive, blender_mesh, modifiers, export_settings): indices = blender_primitive['indices'] new_indices = [] # Reverse order of indices so game will render the proper front face for i in range(0, len(indices), 3): new_indices.append(indices[i + 2]) new_indices.append(indices[i + 1]) new_indices.append(indices[i + 0]) indices = new_indices # NOTE: Values used by some graphics APIs as "primitive restart" values are disallowed. # Specifically, the values 65535 (in UINT16) and 4294967295 (in UINT32) cannot be used as indices. # https://github.com/KhronosGroup/glTF/issues/1142 # https://github.com/KhronosGroup/glTF/pull/1476/files # Also, UINT8 mode is not supported: # https://github.com/KhronosGroup/glTF/issues/1471 max_index = max(indices) assert max_index < 65535 # if max_index >= 65530: # MSFS is expecting an unsigned short component_type = gltf2_io_constants.ComponentType.UnsignedShort # if max_index < 65535: # component_type = gltf2_io_constants.ComponentType.UnsignedShort # elif max_index < 4294967295: # component_type = gltf2_io_constants.ComponentType.UnsignedInt # else: # print_console('ERROR', 'A mesh contains too many vertices (' + str(max_index) + ') and needs to be split before export.') # return None element_type = gltf2_io_constants.DataType.Scalar return gltf2_io.Accessor(buffer_view=indices, byte_offset=None, component_type=component_type, count=len(indices), extensions=None, extras=None, max=None, min=None, name=None, normalized=None, sparse=None, type=element_type)
def gather_accessor(buffer_view: gltf2_io_binary_data.BinaryData, component_type: gltf2_io_constants.ComponentType, count, max, min, type: gltf2_io_constants.DataType, export_settings) -> gltf2_io.Accessor: return gltf2_io.Accessor(buffer_view=buffer_view, byte_offset=None, component_type=component_type, count=count, extensions=None, extras=None, max=list(max) if max is not None else None, min=list(min) if min is not None else None, name=None, normalized=None, sparse=None, type=type)
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_position(blender_primitive, export_settings): position = blender_primitive["attributes"]["POSITION"] componentType = gltf2_io_constants.ComponentType.Float return { "POSITION": gltf2_io.Accessor( buffer_view=gltf2_io_binary_data.BinaryData.from_list(position, componentType), byte_offset=None, component_type=componentType, count=len(position) // gltf2_io_constants.DataType.num_elements(gltf2_io_constants.DataType.Vec3), extensions=None, extras=None, max=gltf2_blender_utils.max_components(position, gltf2_io_constants.DataType.Vec3), min=gltf2_blender_utils.min_components(position, gltf2_io_constants.DataType.Vec3), name=None, normalized=None, sparse=None, type=gltf2_io_constants.DataType.Vec3 ) }
def __gather_normal(blender_primitive, export_settings): if export_settings['gltf_normals']: normal = blender_primitive["attributes"]['NORMAL'] return { "NORMAL": gltf2_io.Accessor( buffer_view=gltf2_io_binary_data.BinaryData.from_list(normal, gltf2_io_constants.ComponentType.Float), byte_offset=None, component_type=gltf2_io_constants.ComponentType.Float, count=len(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 ) } return {}
def __get_keyframe_accessor(frame_start, frame_end, frame_step): # Gets an accessor for a range of keyframes. Used for sampler.input. fps = bpy.context.scene.render.fps keyframes = [ frame / fps for frame in range(frame_start, frame_end, frame_step) ] keyframe_data = array.array('f', keyframes).tobytes() return gltf2_io.Accessor( buffer_view=gltf2_io_binary_data.BinaryData(keyframe_data), component_type=gltf2_io_constants.ComponentType.Float, type=gltf2_io_constants.DataType.Scalar, count=len(keyframes), min=[keyframes[0]], max=[keyframes[-1]], byte_offset=None, extensions=None, extras=None, name='accessorAnimationInput', normalized=None, sparse=None, )
def __gather_input(action_group: bpy.types.ActionGroup, blender_object: bpy.types.Object, export_settings) -> gltf2_io.Accessor: """Gather the key time codes""" keyframes = __gather_keyframes(action_group, export_settings) times = [k.seconds for k in keyframes] return gltf2_io.Accessor( buffer_view=gltf2_io_binary_data.BinaryData.from_list( times, gltf2_io_constants.ComponentType.Float), byte_offset=None, component_type=gltf2_io_constants.ComponentType.Float, count=len(times), extensions=None, extras=None, max=[max(times)], min=[min(times)], name=None, normalized=None, sparse=None, type=gltf2_io_constants.DataType.Scalar)
def __gather_tangent(blender_primitive, export_settings): if export_settings['gltf_tangents']: if blender_primitive["attributes"].get('TANGENT') is not None: tangent = blender_primitive["attributes"]['TANGENT'] return { "TANGENT": gltf2_io.Accessor( buffer_view=gltf2_io_binary_data.BinaryData.from_list(tangent, gltf2_io_constants.ComponentType.Float), byte_offset=None, component_type=gltf2_io_constants.ComponentType.Float, count=len(tangent) // gltf2_io_constants.DataType.num_elements(gltf2_io_constants.DataType.Vec4), extensions=None, extras=None, max=None, min=None, name=None, normalized=None, sparse=None, type=gltf2_io_constants.DataType.Vec4 ) } return {}
def __gather_indices(blender_primitive, blender_mesh, modifiers, export_settings): indices = blender_primitive['indices'] # NOTE: Values used by some graphics APIs as "primitive restart" values are disallowed. # Specifically, the values 65535 (in UINT16) and 4294967295 (in UINT32) cannot be used as indices. # https://github.com/KhronosGroup/glTF/issues/1142 # https://github.com/KhronosGroup/glTF/pull/1476/files # Also, UINT8 mode is not supported: # https://github.com/KhronosGroup/glTF/issues/1471 max_index = max(indices) if max_index < 65535: component_type = gltf2_io_constants.ComponentType.UnsignedShort elif max_index < 4294967295: component_type = gltf2_io_constants.ComponentType.UnsignedInt else: print_console( 'ERROR', 'A mesh contains too many vertices (' + str(max_index) + ') and needs to be split before export.') return None element_type = gltf2_io_constants.DataType.Scalar binary_data = gltf2_io_binary_data.BinaryData.from_list( indices, component_type) return gltf2_io.Accessor( buffer_view=binary_data, byte_offset=None, component_type=component_type, count=len(indices) // gltf2_io_constants.DataType.num_elements(element_type), extensions=None, extras=None, max=None, min=None, name=None, normalized=None, sparse=None, type=element_type)
def __encode_output_accessor(values, path): # Encodes a list of T, R, or S (Vector/Quaternion) values to an accessor. vals = [x for val in values for x in val] vals_data = array.array('f', vals).tobytes() name = { 'translation': 'accessorAnimationPositions', 'rotation': 'accessorAnimationRotations', 'scale': 'accessorAnimationScales' }.get(path) return gltf2_io.Accessor( buffer_view=gltf2_io_binary_data.BinaryData(vals_data), component_type=gltf2_io_constants.ComponentType.Float, type=gltf2_io_constants.DataType.vec_type_from_num(len(values[0])), count=len(values), min=None, max=None, byte_offset=None, extensions=None, extras=None, name=name, normalized=None, sparse=None, )
def array_to_accessor(array, component_type, data_type, include_max_and_min=False): dtype = gltf2_io_constants.ComponentType.to_numpy_dtype(component_type) num_elems = gltf2_io_constants.DataType.num_elements(data_type) if type(array) is not np.ndarray: array = np.array(array, dtype=dtype) array = array.reshape(len(array) // num_elems, num_elems) if num_elems == 1 and len(array.shape) == 1: array = array.reshape(len(array), 1) assert array.dtype == dtype assert array.shape[1] == num_elems amax = None amin = None if include_max_and_min: amax = np.amax(array, axis=0).tolist() amin = np.amin(array, axis=0).tolist() return gltf2_io.Accessor( buffer_view=gltf2_io_binary_data.BinaryData(array.tobytes()), byte_offset=None, component_type=component_type, count=len(array), extensions=None, extras=None, max=amax, min=amin, name=None, normalized=None, sparse=None, type=data_type, )
def __gather_indices(blender_primitive, export_settings): indices = blender_primitive['indices'] max_index = max(indices) if max_index < (1 << 8): component_type = gltf2_io_constants.ComponentType.UnsignedByte elif max_index < (1 << 16): component_type = gltf2_io_constants.ComponentType.UnsignedShort elif max_index < (1 << 32): component_type = gltf2_io_constants.ComponentType.UnsignedInt else: gltf2_io_debug.print_console('ERROR', 'Invalid max_index: ' + str(max_index)) return None if export_settings['gltf_force_indices']: component_type = gltf2_io_constants.ComponentType.from_legacy_define( export_settings['gltf_indices']) element_type = gltf2_io_constants.DataType.Scalar binary_data = gltf2_io_binary_data.BinaryData.from_list( indices, component_type) return gltf2_io.Accessor( buffer_view=binary_data, byte_offset=None, component_type=component_type, count=len(indices) // gltf2_io_constants.DataType.num_elements(element_type), extensions=None, extras=None, max=None, min=None, name=None, normalized=None, sparse=None, type=element_type)
def __gather_texcoord(blender_primitive, export_settings): attributes = {} if export_settings['gltf_texcoords']: texcoord_index = 0 texcoord_id = 'TEXCOORD_' + str(texcoord_index) while blender_primitive["attributes"].get(texcoord_id) is not None: texcoord = blender_primitive["attributes"][texcoord_id] attributes[texcoord_id] = gltf2_io.Accessor( buffer_view=gltf2_io_binary_data.BinaryData.from_list(texcoord, gltf2_io_constants.ComponentType.Float), byte_offset=None, component_type=gltf2_io_constants.ComponentType.Float, count=len(texcoord) // gltf2_io_constants.DataType.num_elements(gltf2_io_constants.DataType.Vec2), extensions=None, extras=None, max=None, min=None, name=None, normalized=None, sparse=None, type=gltf2_io_constants.DataType.Vec2 ) texcoord_index += 1 texcoord_id = 'TEXCOORD_' + str(texcoord_index) return attributes
def __gather_colors(blender_primitive, export_settings): attributes = {} if export_settings['gltf_colors']: color_index = 0 color_id = 'COLOR_' + str(color_index) while blender_primitive["attributes"].get(color_id) is not None: internal_color = blender_primitive["attributes"][color_id] attributes[color_id] = gltf2_io.Accessor( buffer_view=gltf2_io_binary_data.BinaryData.from_list(internal_color, gltf2_io_constants.ComponentType.Float), byte_offset=None, component_type=gltf2_io_constants.ComponentType.Float, count=len(internal_color) // gltf2_io_constants.DataType.num_elements(gltf2_io_constants.DataType.Vec4), extensions=None, extras=None, max=None, min=None, name=None, normalized=None, sparse=None, type=gltf2_io_constants.DataType.Vec4 ) color_index += 1 color_id = 'COLOR_' + str(color_index) return attributes
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_skins(blender_primitive, export_settings, asobo_vertex_type): attributes = {} if export_settings[gltf2_blender_export_keys.SKINS]: bone_set_index = 0 joint_id = 'JOINTS_' + str(bone_set_index) weight_id = 'WEIGHTS_' + str(bone_set_index) while blender_primitive["attributes"].get( joint_id) and blender_primitive["attributes"].get(weight_id): if bone_set_index >= 1: if not export_settings['gltf_all_vertex_influences']: gltf2_io_debug.print_console( "WARNING", "There are more than 4 joint vertex influences." "The 4 with highest weight will be used (and normalized)." ) break # joints internal_joint = blender_primitive["attributes"][joint_id] joint = gltf2_io.Accessor( buffer_view=internal_joint, byte_offset=None, component_type=gltf2_io_constants.ComponentType.UnsignedShort, count=len(internal_joint) // gltf2_io_constants.DataType.num_elements( gltf2_io_constants.DataType.Vec4), extensions=None, extras=None, max=None, min=None, name=None, normalized=None, sparse=None, type=gltf2_io_constants.DataType.Vec4) attributes[joint_id] = joint # weights internal_weight = blender_primitive["attributes"][weight_id] # normalize first 4 weights, when not exporting all influences, except BLEND1 since it's already technically normalized if not export_settings[ 'gltf_all_vertex_influences'] and not asobo_vertex_type == 'BLEND1': for idx in range(0, len(internal_weight), 4): weight_slice = internal_weight[idx:idx + 4] total = sum(weight_slice) if total > 0: factor = 1.0 / total internal_weight[idx:idx + 4] = [ w * factor for w in weight_slice ] weight_data_type = gltf2_io_constants.DataType.Scalar if asobo_vertex_type == 'BLEND1' else gltf2_io_constants.DataType.Vec4 component_type = gltf2_io_constants.ComponentType.Float if asobo_vertex_type == 'BLEND1' else gltf2_io_constants.ComponentType.UnsignedShort weight = gltf2_io.Accessor( buffer_view=internal_weight if asobo_vertex_type == 'BLEND1' else [round(x * 65535.0) for x in internal_weight], byte_offset=None, component_type=component_type, count=len(internal_weight) // gltf2_io_constants.DataType.num_elements(weight_data_type), extensions=None, extras=None, max=None, min=None, name=None, normalized=True if component_type == 5121 or component_type == 5123 else None, # only normalized for unsigned bytes and unsigned shorts sparse=None, type=weight_data_type) attributes[weight_id] = weight bone_set_index += 1 joint_id = 'JOINTS_' + str(bone_set_index) weight_id = 'WEIGHTS_' + str(bone_set_index) return attributes
def __gather_targets(blender_primitive, blender_mesh, modifiers, export_settings): if export_settings[MORPH]: targets = [] 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: 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_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 return targets return None
def __gather_skins(blender_primitive, export_settings): attributes = {} if export_settings[gltf2_blender_export_keys.SKINS]: bone_set_index = 0 joint_id = 'JOINTS_' + str(bone_set_index) weight_id = 'WEIGHTS_' + str(bone_set_index) while blender_primitive["attributes"].get( joint_id) and blender_primitive["attributes"].get(weight_id): if bone_set_index >= 1: if not export_settings['gltf_all_vertex_influences']: gltf2_io_debug.print_console( "WARNING", "There are more than 4 joint vertex influences." "The 4 with highest weight will be used (and normalized)." ) break # joints internal_joint = blender_primitive["attributes"][joint_id] joint = gltf2_io.Accessor( buffer_view=gltf2_io_binary_data.BinaryData.from_list( internal_joint, gltf2_io_constants.ComponentType.UnsignedShort), byte_offset=None, component_type=gltf2_io_constants.ComponentType.UnsignedShort, count=len(internal_joint) // gltf2_io_constants.DataType.num_elements( gltf2_io_constants.DataType.Vec4), extensions=None, extras=None, max=None, min=None, name=None, normalized=None, sparse=None, type=gltf2_io_constants.DataType.Vec4) attributes[joint_id] = joint # weights internal_weight = blender_primitive["attributes"][weight_id] # normalize first 4 weights, when not exporting all influences if not export_settings['gltf_all_vertex_influences']: for idx in range(0, len(internal_weight), 4): weight_slice = internal_weight[idx:idx + 4] total = sum(weight_slice) if total > 0: factor = 1.0 / total internal_weight[idx:idx + 4] = [ w * factor for w in weight_slice ] weight = gltf2_io.Accessor( buffer_view=gltf2_io_binary_data.BinaryData.from_list( internal_weight, gltf2_io_constants.ComponentType.Float), byte_offset=None, component_type=gltf2_io_constants.ComponentType.Float, count=len(internal_weight) // gltf2_io_constants.DataType.num_elements( gltf2_io_constants.DataType.Vec4), extensions=None, extras=None, max=None, min=None, name=None, normalized=None, sparse=None, type=gltf2_io_constants.DataType.Vec4) attributes[weight_id] = weight bone_set_index += 1 joint_id = 'JOINTS_' + str(bone_set_index) weight_id = 'WEIGHTS_' + str(bone_set_index) return attributes
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 custom_data_array_to_accessor(array): if type(array) is not np.ndarray: array = np.array(array, dtype=np.float32) assert array.dtype == np.float32 assert len(array.shape) == 1 # Calculate how big a sparse array would be and switch to it if smaller indices_nonzero = np.nonzero(array)[0] num_nonzero = len(indices_nonzero) if num_nonzero == 0: return gltf2_io.Accessor( count=len(array), component_type=gltf2_io_constants.ComponentType.Float, type=gltf2_io_constants.DataType.Scalar, buffer_view=None, byte_offset=None, extensions=None, extras=None, max=None, min=None, name=None, normalized=None, sparse=None, ) index_size = (1 if indices_nonzero[-1] < 256 else 2 if indices_nonzero[-1] < 65536 else 4) value_size = 4 # float32 dense_bin_size = len(array) * value_size sparse_bin_size = num_nonzero * (index_size + value_size) bin_size_increase = sparse_bin_size - dense_bin_size json_size_increase = 160 # approximate net_size_increase = bin_size_increase + json_size_increase if net_size_increase >= 0: # Dense is better return array_to_accessor( array, component_type=gltf2_io_constants.ComponentType.Float, data_type=gltf2_io_constants.DataType.Scalar, ) index_type = (gltf2_io_constants.ComponentType.UnsignedByte if index_size == 1 else gltf2_io_constants.ComponentType.UnsignedShort if index_size == 2 else gltf2_io_constants.ComponentType.UnsignedInt) index_dtype = gltf2_io_constants.ComponentType.to_numpy_dtype(index_type) indices_nonzero = indices_nonzero.astype(index_dtype) values_nonzero = array[indices_nonzero] return gltf2_io.Accessor( buffer_view=None, byte_offset=None, component_type=gltf2_io_constants.ComponentType.Float, count=len(array), extensions=None, extras=None, max=None, min=None, name=None, normalized=None, type=gltf2_io_constants.DataType.Scalar, sparse=gltf2_io.AccessorSparse( count=num_nonzero, indices=gltf2_io.AccessorSparseIndices( buffer_view=gltf2_io_binary_data.BinaryData( indices_nonzero.tobytes()), component_type=index_type, byte_offset=None, extensions=None, extras=None, ), values=gltf2_io.AccessorSparseValues( buffer_view=gltf2_io_binary_data.BinaryData( values_nonzero.tobytes()), byte_offset=None, extensions=None, extras=None, ), extensions=None, extras=None, ), )