def __gather_indices(blender_primitive, blender_mesh, modifiers, export_settings): indices = blender_primitive.get('indices') if indices is None: return None # 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 = indices.max() if max_index < 65535: component_type = gltf2_io_constants.ComponentType.UnsignedShort indices = indices.astype(np.uint16, copy=False) elif max_index < 4294967295: component_type = gltf2_io_constants.ComponentType.UnsignedInt indices = indices.astype(np.uint32, copy=False) 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(indices.tobytes()) return gltf2_blender_gather_accessors.gather_accessor( binary_data, component_type, len(indices), None, None, element_type, export_settings)
def __gather_input(channels: typing.Tuple[bpy.types.FCurve], 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 key time codes.""" 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) times = [k.seconds for k in keyframes] return gltf2_blender_gather_accessors.gather_accessor( gltf2_io_binary_data.BinaryData.from_list(times, gltf2_io_constants.ComponentType.Float), gltf2_io_constants.ComponentType.Float, len(times), tuple([max(times)]), tuple([min(times)]), gltf2_io_constants.DataType.Scalar, export_settings )
def __gather_input(channels: typing.Tuple[bpy.types.FCurve], blender_obj_uuid: str, is_armature: bool, 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, force_range: bool, action_name, driver_obj_uuid, node_channel_is_animated: bool, export_settings) -> gltf2_io.Accessor: """Gather the key time codes.""" keyframes, is_baked = gltf2_blender_gather_animation_sampler_keyframes.gather_keyframes( blender_obj_uuid, is_armature, channels, non_keyed_values, bake_bone, bake_channel, bake_range_start, bake_range_end, force_range, action_name, driver_obj_uuid, node_channel_is_animated, export_settings) if keyframes is None: # After check, no need to animation this node return None times = [k.seconds for k in keyframes] return gltf2_blender_gather_accessors.gather_accessor( gltf2_io_binary_data.BinaryData.from_list( times, gltf2_io_constants.ComponentType.Float), gltf2_io_constants.ComponentType.Float, len(times), tuple([max(times)]), tuple([min(times)]), gltf2_io_constants.DataType.Scalar, export_settings)
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_blender_gather_accessors.gather_accessor( binary_data, component_type, len(indices) // gltf2_io_constants.DataType.num_elements(element_type), None, None, element_type, export_settings )
def __gather_inverse_bind_matrices(blender_object, export_settings): bones = [group.name for group in blender_object.vertex_groups] # get bones in skin modifiers = {m.type: m for m in blender_object.modifiers} armature = modifiers["ARMATURE"].object 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))) if export_settings['gltf_def_bones'] is False: # build the hierarchy of nodes out of the bones root_bones = [] for blender_bone in armature.pose.bones: if not blender_bone.parent: root_bones.append(blender_bone) else: _, children_, root_bones = get_bone_tree(None, armature) matrices = [] # traverse the matrices in the same order as the joints and compute the inverse bind matrix def __collect_matrices(bone): if bone.name in bones: inverse_bind_matrix = (axis_basis_change @ ( blender_object.matrix_world @ bone.bone.matrix_local) @ axis_basis_change.inverted()).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(armature.pose.bones[child]) # start with the "root" bones and recurse into children, in the same ordering as the how joints are gathered for root_bone in root_bones: __collect_matrices(root_bone) # flatten the matrices inverse_matrices = [] for matrix in matrices: for column in range(0, 4): for row in range(0, 4): inverse_matrices.append(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, 'accessorInverseBindMatrices', export_settings)
def __gather_input(channels: typing.Tuple[bpy.types.FCurve], export_settings) -> gltf2_io.Accessor: """Gather the key time codes.""" keyframes = gltf2_blender_gather_animation_sampler_keyframes.gather_keyframes( channels, export_settings) times = [k.seconds for k in keyframes] return gltf2_blender_gather_accessors.gather_accessor( gltf2_io_binary_data.BinaryData.from_list( times, gltf2_io_constants.ComponentType.Float), gltf2_io_constants.ComponentType.Float, len(times), tuple([max(times)]), tuple([min(times)]), gltf2_io_constants.DataType.Scalar, export_settings)
def __gather_inverse_bind_matrices(blender_object, mesh_object, export_settings): 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))) # build the hierarchy of nodes out of the bones root_bones = [] for blender_bone in blender_object.pose.bones: if not blender_bone.parent: root_bones.append(blender_bone) matrices = [] # traverse the matrices in the same order as the joints and compute the inverse bind matrix def __collect_matrices(bone): 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, bone.bone.matrix_local ) ).inverted() matrices.append(inverse_bind_matrix) for child in bone.children: __collect_matrices(child) # start with the "root" bones and recurse into children, in the same ordering as the how joints are gathered for root_bone in root_bones: __collect_matrices(root_bone) # flatten the matrices inverse_matrices = [] for matrix in matrices: for column in range(0, 4): for row in range(0, 4): inverse_matrices.append(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_input(channels: typing.Tuple[bpy.types.FCurve], blender_object_if_armature: typing.Optional[bpy.types.Object], export_settings ) -> gltf2_io.Accessor: """Gather the key time codes.""" keyframes = gltf2_blender_gather_animation_sampler_keyframes.gather_keyframes(blender_object_if_armature, channels, export_settings) times = [k.seconds for k in keyframes] return gltf2_blender_gather_accessors.gather_accessor( gltf2_io_binary_data.BinaryData.from_list(times, gltf2_io_constants.ComponentType.Float), gltf2_io_constants.ComponentType.Float, len(times), tuple([max(times)]), tuple([min(times)]), gltf2_io_constants.DataType.Scalar, export_settings )
def __gather_inverse_bind_matrices(armature_uuid, export_settings): blender_armature_object = export_settings['vtree'].nodes[ armature_uuid].blender_object 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))) # store matrix_world of armature in case we need to add a neutral bone export_settings['vtree'].nodes[ armature_uuid].matrix_world_armature = blender_armature_object.matrix_world.copy( ) bones_uuid = export_settings['vtree'].get_all_bones(armature_uuid) def __collect_matrices(bone): inverse_bind_matrix = ( axis_basis_change @ (blender_armature_object.matrix_world @ bone.bone.matrix_local)).inverted_safe() matrices.append(inverse_bind_matrix) matrices = [] for b in bones_uuid: __collect_matrices(blender_armature_object.pose.bones[ export_settings['vtree'].nodes[b].blender_bone.name]) # flatten the matrices inverse_matrices = [] for matrix in matrices: for column in range(0, 4): for row in range(0, 4): inverse_matrices.append(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_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 add_neutral_bones(self): for n in [ n for n in self.nodes.values() if n.armature is not None and n.blender_type == VExportNode. OBJECT and hasattr(self.nodes[n.armature], "need_neutral_bone") ]: #all skin meshes objects where neutral bone is needed # First add a new node axis_basis_change = Matrix.Identity(4) if self.export_settings[gltf2_blender_export_keys.YUP]: 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))) trans, rot, sca = axis_basis_change.decompose() 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]] neutral_bone = gltf2_io.Node(camera=None, children=None, extensions=None, extras=None, matrix=None, mesh=None, name='neutral_bone', rotation=rotation, scale=scale, skin=None, translation=translation, weights=None) # Add it to child list of armature self.nodes[n.armature].node.children.append(neutral_bone) # Add it to joint list n.node.skin.joints.append(neutral_bone) # Need to add an InverseBindMatrix array = BinaryData.decode_accessor_internal( n.node.skin.inverse_bind_matrices) axis_basis_change = Matrix.Identity(4) if self.export_settings[gltf2_blender_export_keys.YUP]: 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))) inverse_bind_matrix = (axis_basis_change @ self.nodes[ n.armature].matrix_world_armature).inverted_safe() matrix = [] for column in range(0, 4): for row in range(0, 4): matrix.append(inverse_bind_matrix[row][column]) array = np.append(array, np.array([matrix]), axis=0) binary_data = gltf2_io_binary_data.BinaryData.from_list( array.flatten(), gltf2_io_constants.ComponentType.Float) n.node.skin.inverse_bind_matrices = gltf2_blender_gather_accessors.gather_accessor( binary_data, gltf2_io_constants.ComponentType.Float, len(array.flatten()) // gltf2_io_constants.DataType.num_elements( gltf2_io_constants.DataType.Mat4), None, None, gltf2_io_constants.DataType.Mat4, self.export_settings)