Lots of typing fixes and simplifications (using list and dict instead of the typing.List etc.)

This commit is contained in:
Colin Basnett
2026-02-03 00:00:47 -08:00
parent 69fc702393
commit 63ee31bb00
14 changed files with 82 additions and 77 deletions

View File

@@ -1,7 +1,7 @@
from bpy.types import Action, AnimData, Context, Object, PoseBone
from psk_psa_py.psa.data import Psa
from typing import Dict, List, Optional, Tuple
from typing import Tuple
from mathutils import Matrix, Quaternion, Vector
from ..shared.helpers import PsxBoneCollection, convert_bpy_quaternion_to_psx_quaternion, convert_vector_to_vector3, create_psx_bones, get_coordinate_system_transform
@@ -10,7 +10,7 @@ from ..shared.helpers import PsxBoneCollection, convert_bpy_quaternion_to_psx_qu
class PsaBuildSequence:
class NlaState:
def __init__(self):
self.action: Optional[Action] = None
self.action: Action | None = None
self.frame_start: int = 0
self.frame_end: int = 0
@@ -22,16 +22,16 @@ class PsaBuildSequence:
self.compression_ratio: float = 1.0
self.key_quota: int = 0
self.fps: float = 30.0
self.group: Optional[str] = None
self.group: str | None = None
class PsaBuildOptions:
def __init__(self):
self.armature_objects: List[Object] = []
self.animation_data: Optional[AnimData] = None
self.sequences: List[PsaBuildSequence] = []
self.armature_objects: list[Object] = []
self.animation_data: AnimData | None = None
self.sequences: list[PsaBuildSequence] = []
self.bone_filter_mode: str = 'ALL'
self.bone_collection_indices: List[PsxBoneCollection] = []
self.bone_collection_indices: list[PsxBoneCollection] = []
self.sequence_name_prefix: str = ''
self.sequence_name_suffix: str = ''
self.scale = 1.0
@@ -307,7 +307,6 @@ def build_psa(context: Context, options: PsaBuildOptions) -> Psa:
options.export_space,
export_bone.scale,
coordinate_system_transform=coordinate_system_transform
# has_false_root_bone=psx_bone_create_result.has_false_root_bone,
)
last_frame_bone_poses.append((location, rotation))
@@ -331,7 +330,6 @@ def build_psa(context: Context, options: PsaBuildOptions) -> Psa:
export_space=options.export_space,
scale=export_bone.scale,
coordinate_system_transform=coordinate_system_transform,
# has_false_root_bone=psx_bone_create_result.has_false_root_bone,
)
next_frame_bone_poses.append((location, rotation))
@@ -359,7 +357,6 @@ def build_psa(context: Context, options: PsaBuildOptions) -> Psa:
export_space=options.export_space,
scale=export_bone.scale,
coordinate_system_transform=coordinate_system_transform,
# has_false_root_bone=psx_bone_create_result.has_false_root_bone,
)
add_key(location, rotation)

View File

@@ -1,6 +1,5 @@
import re
from configparser import ConfigParser
from typing import Dict, List
REMOVE_TRACK_LOCATION = (1 << 0)
REMOVE_TRACK_ROTATION = (1 << 1)
@@ -8,7 +7,7 @@ REMOVE_TRACK_ROTATION = (1 << 1)
class PsaConfig:
def __init__(self):
self.sequence_bone_flags: Dict[str, Dict[int, int]] = dict()
self.sequence_bone_flags: dict[str, dict[int, int]] = dict()
def _load_config_file(file_path: str) -> ConfigParser:
@@ -48,7 +47,7 @@ def _get_bone_flags_from_value(value: str) -> int:
return 0
def read_psa_config(psa_sequence_names: List[str], file_path: str) -> PsaConfig:
def read_psa_config(psa_sequence_names: list[str], file_path: str) -> PsaConfig:
psa_config = PsaConfig()
config = _load_config_file(file_path)

View File

@@ -1,6 +1,6 @@
from abc import abstractmethod
from collections import Counter
from typing import List, Iterable, Dict, Protocol, Sequence, Tuple, cast as typing_cast
from typing import Iterable, Sequence, Tuple, cast as typing_cast
import bpy
import re
@@ -19,7 +19,7 @@ from .ui import PSA_UL_export_sequences
from ..builder import build_psa, PsaBuildSequence, PsaBuildOptions
from psk_psa_py.psa.writer import write_psa_to_file
from ...shared.helpers import get_collection_export_operator_from_context, get_collection_from_context, get_psk_input_objects_for_collection, populate_bone_collection_list, get_nla_strips_in_frame_range, PsxBoneCollection
from ...shared.types import BpyCollectionProperty, PSX_PG_action_export
from ...shared.types import PSX_PG_action_export
from ...shared.ui import draw_bone_filter_mode
from ...shared.operators import PSK_OT_bone_collection_list_populate, PSK_OT_bone_collection_list_select_all
@@ -179,7 +179,7 @@ def get_sequence_compression_ratio(
def get_timeline_marker_sequence_frame_ranges(
animation_data: AnimData,
scene: Scene,
marker_names: List[str],
marker_names: list[str],
) -> dict[str, tuple[int, int]]:
# Timeline markers need to be sorted so that we can determine the sequence start and end positions.
sequence_frame_ranges: dict[str, tuple[int, int]] = dict()
@@ -240,7 +240,7 @@ def get_sequences_from_action(action: Action):
def get_sequences_from_action_pose_markers(
action: Action,
pose_markers: List[TimelineMarker],
pose_markers: list[TimelineMarker],
pose_marker: TimelineMarker,
pose_marker_index: int,
):
@@ -259,7 +259,7 @@ def get_sequences_from_action_pose_markers(
yield from get_sequences_from_name_and_frame_range(sequence_name, frame_start, frame_end)
def get_visible_sequences(pg: PsaExportMixin, sequences) -> List[PsaExportSequenceMixin]:
def get_visible_sequences(pg: PsaExportMixin, sequences) -> list[PsaExportSequenceMixin]:
visible_sequences = []
for i, flag in enumerate(filter_sequences(pg, sequences)):
if bool(flag & (1 << 30)):
@@ -471,7 +471,7 @@ def create_psa_export_options(context: Context, armature_objects: Sequence[Objec
raise RuntimeError(f'No armatures')
animation_data = armature_objects[0].animation_data
export_sequences: List[PsaBuildSequence] = []
export_sequences: list[PsaBuildSequence] = []
# TODO: this needs to be changed so that we iterate over all of the armature objects?
# do we need to check for primary key? (data vs. object?)
@@ -507,7 +507,7 @@ def create_psa_export_options(context: Context, armature_objects: Sequence[Objec
export_sequences.append(export_sequence)
case 'TIMELINE_MARKERS':
for marker_item in filter(lambda x: x.is_selected, pg.marker_list):
nla_strips_actions: List[Action] = []
nla_strips_actions: list[Action] = []
for nla_strip in get_nla_strips_in_frame_range(animation_data, marker_item.frame_start, marker_item.frame_end):
if nla_strip.action:
nla_strips_actions.append(nla_strip.action)
@@ -584,7 +584,7 @@ class PSA_OT_export(Operator, ExportHelper):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.armature_objects: List[Object] = []
self.armature_objects: list[Object] = []
@classmethod
def poll(cls, context):

View File

@@ -1,7 +1,7 @@
import re
import sys
from fnmatch import fnmatch
from typing import List, Optional, Sequence
from typing import Sequence
import bpy
from bpy.props import (
BoolProperty,
@@ -58,7 +58,7 @@ class PSA_PG_export_sequence(PsaExportSequenceMixin):
def get_sequences_from_name_and_frame_range(name: str, frame_start: int, frame_end: int):
# Check for loop
anims: List[tuple[str, int, int]] = []
anims: list[tuple[str, int, int]] = []
loop_pattern = r'\@(\d+)\:(.+)'
loop_match = re.match(loop_pattern, name)
if loop_match:
@@ -107,7 +107,7 @@ def nla_track_update_cb(self: 'PSA_PG_export', context: Context) -> None:
strip.frame_end = frame_end
def get_animation_data(pg: 'PSA_PG_export', context: Context) -> Optional[AnimData]:
def get_animation_data(pg: 'PSA_PG_export', context: Context) -> AnimData | None:
animation_data_object = context.object
return animation_data_object.animation_data if animation_data_object else None

View File

@@ -3,7 +3,7 @@ from pathlib import Path
from typing import Iterable
from bpy.props import CollectionProperty, StringProperty
from bpy.types import Context, Event, FileHandler, Object, Operator, OperatorFileListElement
from bpy.types import Context, Event, Object, Operator, OperatorFileListElement
from bpy_extras.io_utils import ImportHelper
from .properties import PsaImportMixin, get_visible_sequences

View File

@@ -1,6 +1,5 @@
import re
from fnmatch import fnmatch
from typing import List
from bpy.props import (
BoolProperty,
@@ -133,7 +132,7 @@ class PSA_PG_import(PropertyGroup):
select_text: PointerProperty(type=Text)
def filter_sequences(pg: PSA_PG_import, sequences) -> List[int]:
def filter_sequences(pg: PSA_PG_import, sequences) -> list[int]:
bitflag_filter_item = 1 << 30
flt_flags = [bitflag_filter_item] * len(sequences)
@@ -167,7 +166,7 @@ def filter_sequences(pg: PSA_PG_import, sequences) -> List[int]:
return flt_flags
def get_visible_sequences(pg: PSA_PG_import, sequences) -> List[PSA_PG_import_action_list_item]:
def get_visible_sequences(pg: PSA_PG_import, sequences) -> list[PSA_PG_import_action_list_item]:
bitflag_filter_item = 1 << 30
visible_sequences = []
for i, flag in enumerate(filter_sequences(pg, sequences)):

View File

@@ -1,4 +1,4 @@
from typing import Sequence, Iterable, List, Optional, cast as typing_cast
from typing import Sequence, Iterable, cast as typing_cast
import bpy
import numpy as np
@@ -32,7 +32,7 @@ class PsaImportOptions(object):
fps_custom: float = 30.0,
fps_source: str = 'SEQUENCE',
psa_config: PsaConfig = PsaConfig(),
sequence_names: Optional[List[str]] = None,
sequence_names: list[str] | None = None,
should_convert_to_samples: bool = False,
should_overwrite: bool = False,
should_stash: bool = False,
@@ -61,13 +61,13 @@ class PsaImportOptions(object):
class ImportBone(object):
def __init__(self, psa_bone: PsxBone):
self.psa_bone: PsxBone = psa_bone
self.parent: Optional[ImportBone] = None
self.armature_bone: Optional[Bone] = None
self.pose_bone: Optional[PoseBone] = None
self.parent: ImportBone | None = None
self.armature_bone: Bone | None = None
self.pose_bone: PoseBone | None = None
self.original_location: Vector = Vector()
self.original_rotation: Quaternion = Quaternion()
self.post_rotation: Quaternion = Quaternion()
self.fcurves: List[FCurve] = []
self.fcurves: list[FCurve] = []
def _calculate_fcurve_data(import_bone: ImportBone, key_data: Sequence[float]):
@@ -90,10 +90,10 @@ def _calculate_fcurve_data(import_bone: ImportBone, key_data: Sequence[float]):
class PsaImportResult:
def __init__(self):
self.warnings: List[str] = []
self.warnings: list[str] = []
def _get_armature_bone_index_for_psa_bone(psa_bone_name: str, armature_bone_names: List[str], bone_mapping: BoneMapping) -> Optional[int]:
def _get_armature_bone_index_for_psa_bone(psa_bone_name: str, armature_bone_names: list[str], bone_mapping: BoneMapping) -> int | None:
"""
@param psa_bone_name: The name of the PSA bone.
@param armature_bone_names: The names of the bones in the armature.

View File

@@ -1,9 +1,9 @@
import bmesh
import bpy
import numpy as np
from bpy.types import Armature, Context, Object, Mesh
from bpy.types import Armature, Context, Object, Mesh, Material
from mathutils import Matrix
from typing import Dict, Iterable, List, Optional, cast as typing_cast
from typing import Iterable, Sequence, cast as typing_cast
from psk_psa_py.shared.data import Vector3
from psk_psa_py.psk.data import Psk
from .properties import triangle_type_and_bit_flags_to_poly_flags
@@ -23,10 +23,10 @@ from ..shared.helpers import (
class PskBuildOptions(object):
def __init__(self):
self.bone_filter_mode = 'ALL'
self.bone_collection_indices: List[PsxBoneCollection] = []
self.bone_collection_indices: list[PsxBoneCollection] = []
self.object_eval_state = 'EVALUATED'
self.material_order_mode = 'AUTOMATIC'
self.material_name_list: List[str] = []
self.material_name_list: list[str] = []
self.scale = 1.0
self.export_space = 'WORLD'
self.forward_axis = 'X'
@@ -37,7 +37,7 @@ class PskBuildOptions(object):
class PskBuildResult(object):
def __init__(self, psk: Psk, warnings: list[str]):
self.psk: Psk = psk
self.warnings: List[str] = warnings
self.warnings: list[str] = warnings
def _get_mesh_export_space_matrix(node: ObjectNode | None, export_space: str) -> Matrix:
@@ -70,7 +70,7 @@ def _get_mesh_export_space_matrix(node: ObjectNode | None, export_space: str) ->
assert False, f'Invalid export space: {export_space}'
def _get_material_name_indices(obj: Object, material_names: List[str]) -> Iterable[int]:
def _get_material_name_indices(obj: Object, material_names: list[str]) -> Iterable[int]:
"""
Returns the index of the material in the list of material names.
If the material is not found, the index 0 is returned.
@@ -206,7 +206,7 @@ def build_psk(context: Context, input_objects: PskInputObjects, options: PskBuil
bm.to_mesh(mesh_data)
del bm
evaluated_mesh_object = bpy.data.objects.new('', mesh_data)
mesh_object = evaluated_mesh_object
mesh_object = evaluated_mesh_object
mesh_object.matrix_world = matrix_world
# Extract the scale from the matrix.
@@ -271,6 +271,7 @@ def build_psk(context: Context, input_objects: PskInputObjects, options: PskBuil
for loop_index, loop in enumerate(mesh_data.loops):
wedges.append(Psk.Wedge(point_index=loop.vertex_index + vertex_offset, u=0.0, v=0.0))
# Assign material indices to the wedges.
for triangle in mesh_data.loop_triangles:
for loop_index in triangle.loops:
@@ -315,7 +316,7 @@ def build_psk(context: Context, input_objects: PskInputObjects, options: PskBuil
bone_names = psx_bone_create_result.armature_object_bone_names[armature_object]
vertex_group_names = [x.name for x in mesh_object.vertex_groups]
vertex_group_bone_indices: Dict[int, int] = dict()
vertex_group_bone_indices: dict[int, int] = dict()
for vertex_group_index, vertex_group_name in enumerate(vertex_group_names):
try:
vertex_group_bone_indices[vertex_group_index] = bone_names.index(vertex_group_name) + bone_index_offset

View File

@@ -1,5 +1,5 @@
from pathlib import Path
from typing import Iterable, List, cast as typing_cast
from typing import Iterable, cast as typing_cast
import bpy
from bpy.props import StringProperty
@@ -173,7 +173,7 @@ class PSK_OT_material_list_name_move_down(Operator):
return {'FINISHED'}
def get_sorted_materials_by_names(materials: Iterable[Material], material_names: List[str]) -> List[Material]:
def get_sorted_materials_by_names(materials: Iterable[Material], material_names: list[str]) -> list[Material]:
"""
Sorts the materials by the order of the material names list. Any materials not in the list will be appended to the
end of the list in the order they are found.

View File

@@ -4,7 +4,7 @@ import numpy as np
from bpy.types import Context, Object, VertexGroup, ArmatureModifier, FloatColorAttribute
from mathutils import Matrix, Quaternion, Vector
from typing import List, Optional, cast as typing_cast
from typing import cast as typing_cast
from psk_psa_py.psk.data import Psk
from psk_psa_py.shared.data import PsxBone
@@ -35,12 +35,11 @@ class ImportBone:
def __init__(self, index: int, psk_bone: PsxBone):
self.index: int = index
self.psk_bone: PsxBone = psk_bone
self.parent: Optional[ImportBone] = None
self.parent: ImportBone | None = None
self.local_rotation: Quaternion = Quaternion()
self.local_translation: Vector = Vector()
self.world_rotation_matrix: Matrix = Matrix()
self.world_matrix: Matrix = Matrix()
self.vertex_group = None
self.original_rotation: Quaternion = Quaternion()
self.original_location: Vector = Vector()
self.post_rotation: Quaternion = Quaternion()
@@ -48,9 +47,9 @@ class ImportBone:
class PskImportResult:
def __init__(self):
self.warnings: List[str] = []
self.armature_object: Optional[Object] = None
self.mesh_object: Optional[Object] = None
self.warnings: list[str] = []
self.armature_object: Object | None = None
self.mesh_object: Object | None = None
@property
def root_object(self) -> Object:
@@ -83,7 +82,7 @@ def import_psk(psk: Psk, context: Context, name: str, options: PskImportOptions)
bpy.ops.object.mode_set(mode='EDIT')
import_bones: List[ImportBone] = []
import_bones: list[ImportBone] = []
for bone_index, psk_bone in enumerate(psk.bones):
import_bone = ImportBone(bone_index, psk_bone)
@@ -263,7 +262,7 @@ def import_psk(psk: Psk, context: Context, name: str, options: PskImportOptions)
# Weights
# Get a list of all bones that have weights associated with them.
vertex_group_bone_indices = set(map(lambda weight: weight.bone_index, psk.weights))
vertex_groups: List[Optional[VertexGroup]] = [None] * len(psk.bones)
vertex_groups: list[VertexGroup | None] = [None] * len(psk.bones)
for bone_index, psk_bone in map(lambda x: (x, psk.bones[x]), vertex_group_bone_indices):
vertex_groups[bone_index] = mesh_object.vertex_groups.new(name=psk_bone.name.decode('windows-1252'))

View File

@@ -16,3 +16,8 @@ class PskImportMixin:
should_import_shape_keys: bool
scale: float
bdk_repository_id: str
def triangle_type_and_bit_flags_to_poly_flags(mesh_triangle_type: str, mesh_triangle_bit_flags: set[str]) -> int: ...
def poly_flags_to_triangle_type_and_bit_flags(poly_flags: int) -> tuple[str, set[str]]: ...

View File

@@ -5,7 +5,7 @@ These functions are used to iterate over objects in a collection or view layer i
instances. This is useful for exporters that need to traverse the object hierarchy in a predictable order.
"""
from typing import Optional, Set, Iterable, List
from typing import Iterable
from bpy.types import Collection, Object, ViewLayer, LayerCollection
from mathutils import Matrix
@@ -15,7 +15,7 @@ class DfsObject:
"""
Represents an object in a depth-first search.
"""
def __init__(self, obj: Object, instance_objects: List[Object], matrix_world: Matrix):
def __init__(self, obj: Object, instance_objects: list[Object], matrix_world: Matrix):
self.obj = obj
self.instance_objects = instance_objects
self.matrix_world = matrix_world
@@ -85,9 +85,9 @@ def dfs_collection_objects(collection: Collection, visible_only: bool = False) -
def _dfs_collection_objects_recursive(
collection: Collection,
instance_objects: Optional[List[Object]] = None,
instance_objects: list[Object] | None = None,
matrix_world: Matrix = Matrix.Identity(4),
visited: Optional[Set[Object]]=None
visited: set[Object] | None = None
) -> Iterable[DfsObject]:
"""
Depth-first search of objects in a collection, including recursing into instances.

View File

@@ -1,6 +1,6 @@
import bpy
from collections import Counter
from typing import List, Iterable, Optional, Dict, Tuple, cast as typing_cast
from typing import Iterable, cast as typing_cast
from bpy.types import Armature, AnimData, Collection, Context, Object, ArmatureModifier, SpaceProperties, PropertyGroup
from mathutils import Matrix, Vector, Quaternion as BpyQuaternion
from psk_psa_py.shared.data import PsxBone, Quaternion, Vector3
@@ -102,7 +102,7 @@ def populate_bone_collection_list(
item.is_selected = bone_collection.name in selected_assigned_collection_names if has_selected_collections else True
def get_export_bone_names(armature_object: Object, bone_filter_mode: str, bone_collection_indices: Iterable[int]) -> List[str]:
def get_export_bone_names(armature_object: Object, bone_filter_mode: str, bone_collection_indices: Iterable[int]) -> list[str]:
"""
Returns a sorted list of bone indices that should be exported for the given bone filter mode and bone collections.
@@ -172,9 +172,9 @@ def convert_string_to_cp1252_bytes(string: str) -> bytes:
def create_psx_bones_from_blender_bones(
bones: List[bpy.types.Bone],
bones: list[bpy.types.Bone],
armature_object_matrix_world: Matrix,
) -> List[PsxBone]:
) -> list[PsxBone]:
"""
Creates PSX bones from the given Blender bones.
@@ -183,7 +183,7 @@ def create_psx_bones_from_blender_bones(
# Apply the scale of the armature object to the bone location.
_, _, armature_object_scale = armature_object_matrix_world.decompose()
psx_bones: List[PsxBone] = []
psx_bones: list[PsxBone] = []
for bone in bones:
psx_bone = PsxBone()
psx_bone.name = convert_string_to_cp1252_bytes(bone.name)
@@ -238,10 +238,6 @@ class PsxBoneCreateResult:
self.armature_object_root_bone_indices = armature_object_root_bone_indices
self.armature_object_bone_names = armature_object_bone_names
@property
def has_false_root_bone(self) -> bool:
return len(self.bones) > 0 and self.bones[0].armature_object is None
def convert_vector_to_vector3(vector: Vector) -> Vector3:
"""
@@ -280,7 +276,7 @@ class ObjectNode:
def __init__(self, obj: Object):
self.object = obj
self.parent: ObjectNode | None = None
self.children: List[ObjectNode] = []
self.children: list[ObjectNode] = []
@property
def root(self):
@@ -298,8 +294,8 @@ class ObjectTree:
A tree of the armature objects based on their hierarchy.
'''
def __init__(self, objects: Iterable[Object]):
self.root_nodes: List[ObjectNode] = []
object_node_map: Dict[Object, ObjectNode] = {x: ObjectNode(x) for x in objects}
self.root_nodes: list[ObjectNode] = []
object_node_map: dict[Object, ObjectNode] = {x: ObjectNode(x) for x in objects}
for obj, object_node in object_node_map.items():
if obj.parent in object_node_map:
@@ -334,14 +330,14 @@ class ObjectTree:
def create_psx_bones(
armature_objects: List[Object],
armature_objects: list[Object],
export_space: str = 'WORLD',
root_bone_name: str = 'ROOT',
forward_axis: str = 'X',
up_axis: str = 'Z',
scale: float = 1.0,
bone_filter_mode: str = 'ALL',
bone_collection_indices: Optional[List[PsxBoneCollection]] = None,
bone_collection_indices: list[PsxBoneCollection] | None = None,
bone_collection_primary_key: str = 'OBJECT',
) -> PsxBoneCreateResult:
"""
@@ -366,9 +362,9 @@ def create_psx_bones(
total_bone_count += len(armature_data.bones)
# Store the bone names to be exported for each armature object.
armature_object_bone_names: Dict[Object, List[str]] = dict()
armature_object_bone_names: dict[Object, list[str]] = dict()
for armature_object in armature_objects:
armature_bone_collection_indices: List[int] = []
armature_bone_collection_indices: list[int] = []
match bone_collection_primary_key:
case 'OBJECT':
armature_bone_collection_indices.extend([x.index for x in bone_collection_indices if x.armature_object_name == armature_object.name])
@@ -390,6 +386,13 @@ def create_psx_bones(
armature_data = typing_cast(Armature, armature_object.data)
armature_bones = [armature_data.bones[bone_name] for bone_name in bone_names]
# Ensure that we don't have multiple root bones in this armature.
root_bone_count = sum(1 for bone in armature_bones if bone.parent is None)
if root_bone_count > 1:
raise RuntimeError(f'Armature object \'{armature_object.name}\' has multiple root bones. '
f'Only one root bone is allowed per armature.'
)
armature_psx_bones = create_psx_bones_from_blender_bones(
bones=armature_bones,
armature_object_matrix_world=armature_object.matrix_world,
@@ -611,8 +614,8 @@ from bpy.types import Depsgraph
class PskInputObjects(object):
def __init__(self):
self.mesh_dfs_objects: List[DfsObject] = []
self.armature_objects: List[Object] = []
self.mesh_dfs_objects: list[DfsObject] = []
self.armature_objects: list[Object] = []
def get_materials_for_mesh_objects(depsgraph: Depsgraph, mesh_objects: Iterable[Object]):
@@ -640,7 +643,7 @@ def get_mesh_objects_for_context(context: Context) -> Iterable[DfsObject]:
yield dfs_object
def get_armature_for_mesh_object(mesh_object: Object) -> Optional[Object]:
def get_armature_for_mesh_object(mesh_object: Object) -> Object | None:
if mesh_object.type != 'MESH':
return None
# Get the first armature modifier with a non-empty armature object.

View File

@@ -59,3 +59,5 @@ class PsxBoneExportMixin:
class PSX_PG_scene_export(TransformSourceMixin):
pass
bone_filter_mode_items: tuple[tuple[str, str, str]]