Checkpoint commit
This commit is contained in:
@@ -48,58 +48,40 @@ class PsaBuildOptions:
|
||||
|
||||
|
||||
def _get_pose_bone_location_and_rotation(
|
||||
pose_bone: PoseBone | None,
|
||||
armature_object: Object | None,
|
||||
pose_bone: PoseBone,
|
||||
armature_object: Object,
|
||||
export_space: str,
|
||||
scale: Vector,
|
||||
coordinate_system_transform: Matrix,
|
||||
has_false_root_bone: bool,
|
||||
) -> Tuple[Vector, Quaternion]:
|
||||
is_false_root_bone = pose_bone is None and armature_object is None
|
||||
|
||||
# TODO: this is such a disaster; the false root bone idea needs revising.
|
||||
|
||||
if is_false_root_bone:
|
||||
pose_bone_matrix = coordinate_system_transform
|
||||
elif pose_bone is not None and pose_bone.parent is not None:
|
||||
pose_bone_matrix = pose_bone.matrix
|
||||
pose_bone_parent_matrix = pose_bone.parent.matrix
|
||||
pose_bone_matrix = pose_bone_parent_matrix.inverted() @ pose_bone_matrix
|
||||
if pose_bone.parent is not None:
|
||||
pose_bone_matrix = pose_bone.parent.matrix.inverted() @ pose_bone.matrix
|
||||
else:
|
||||
# Root bone
|
||||
if has_false_root_bone:
|
||||
pose_bone_matrix = armature_object.matrix_world @ pose_bone.matrix
|
||||
else:
|
||||
# Get the bone's pose matrix and transform it into the export space.
|
||||
# In the case of an 'ARMATURE' export space, this will be the inverse of armature object's world matrix.
|
||||
# Otherwise, it will be the identity matrix.
|
||||
match export_space:
|
||||
case 'ARMATURE':
|
||||
pose_bone_matrix = pose_bone.matrix
|
||||
case 'WORLD':
|
||||
pose_bone_matrix = armature_object.matrix_world @ pose_bone.matrix
|
||||
case 'ROOT':
|
||||
pose_bone_matrix = Matrix.Identity(4)
|
||||
case _:
|
||||
assert False, f'Invalid export space: {export_space}'
|
||||
# Get the bone's pose matrix and transform it into the export space.
|
||||
# In the case of an 'ARMATURE' export space, this will be the inverse of armature object's world matrix.
|
||||
# Otherwise, it will be the identity matrix.
|
||||
match export_space:
|
||||
case 'ARMATURE':
|
||||
pose_bone_matrix = pose_bone.matrix
|
||||
case 'WORLD':
|
||||
pose_bone_matrix = armature_object.matrix_world @ pose_bone.matrix
|
||||
case 'ROOT':
|
||||
pose_bone_matrix = Matrix.Identity(4)
|
||||
case _:
|
||||
assert False, f'Invalid export space: {export_space}'
|
||||
|
||||
# The root bone is the only bone that should be transformed by the coordinate system transform, since all
|
||||
# other bones are relative to their parent bones.
|
||||
pose_bone_matrix = coordinate_system_transform @ pose_bone_matrix
|
||||
# The root bone is the only bone that should be transformed by the coordinate system transform, since all
|
||||
# other bones are relative to their parent bones.
|
||||
pose_bone_matrix = coordinate_system_transform @ pose_bone_matrix
|
||||
|
||||
location = pose_bone_matrix.to_translation()
|
||||
rotation = pose_bone_matrix.to_quaternion().normalized()
|
||||
|
||||
# Don't apply scale to the root bone of armatures if we have a false root.
|
||||
if not has_false_root_bone or (pose_bone is None or pose_bone.parent is not None):
|
||||
if pose_bone.parent is not None:
|
||||
# Don't apply scale to the root bone of armatures if we have a false root.
|
||||
# TODO: probably remove this?
|
||||
location *= scale
|
||||
|
||||
if has_false_root_bone:
|
||||
is_child_bone = not is_false_root_bone
|
||||
else:
|
||||
is_child_bone = pose_bone.parent is not None
|
||||
|
||||
if is_child_bone:
|
||||
rotation.conjugate()
|
||||
|
||||
return location, rotation
|
||||
@@ -218,12 +200,12 @@ def build_psa(context: Context, options: PsaBuildOptions) -> Psa:
|
||||
psa.keys.append(key)
|
||||
|
||||
class PsaExportBone:
|
||||
def __init__(self, pose_bone: Optional[PoseBone], armature_object: Optional[Object], scale: Vector):
|
||||
def __init__(self, pose_bone: PoseBone | None, armature_object: Object | None, scale: Vector):
|
||||
self.pose_bone = pose_bone
|
||||
self.armature_object = armature_object
|
||||
self.scale = scale
|
||||
|
||||
armature_scales: Dict[Object, Vector] = {}
|
||||
armature_scales: dict[Object, Vector] = {}
|
||||
|
||||
# Extract the scale from the world matrix of the evaluated armature object.
|
||||
for armature_object in options.armature_objects:
|
||||
@@ -235,7 +217,7 @@ def build_psa(context: Context, options: PsaBuildOptions) -> Psa:
|
||||
# Create a list of export pose bones, in the same order as the bones as they appear in the armature.
|
||||
# The object contains the pose bone, the armature object, and a pre-calculated scaling value to apply to the
|
||||
# locations.
|
||||
export_bones: List[PsaExportBone] = []
|
||||
export_bones: list[PsaExportBone] = []
|
||||
|
||||
for psx_bone, armature_object in psx_bone_create_result.bones:
|
||||
if armature_object is None:
|
||||
@@ -250,11 +232,11 @@ def build_psa(context: Context, options: PsaBuildOptions) -> Psa:
|
||||
match options.sampling_mode:
|
||||
case 'INTERPOLATED':
|
||||
# Used as a store for the last frame's pose bone locations and rotations.
|
||||
last_frame: Optional[int] = None
|
||||
last_frame_bone_poses: List[Tuple[Vector, Quaternion]] = []
|
||||
last_frame: int | None = None
|
||||
last_frame_bone_poses: list[tuple[Vector, Quaternion]] = []
|
||||
|
||||
next_frame: Optional[int] = None
|
||||
next_frame_bone_poses: List[Tuple[Vector, Quaternion]] = []
|
||||
next_frame: int | None = None
|
||||
next_frame_bone_poses: list[tuple[Vector, Quaternion]] = []
|
||||
|
||||
for _ in range(frame_count):
|
||||
if last_frame is None or last_frame != int(frame):
|
||||
@@ -275,8 +257,8 @@ def build_psa(context: Context, options: PsaBuildOptions) -> Psa:
|
||||
export_bone.armature_object,
|
||||
options.export_space,
|
||||
export_bone.scale,
|
||||
coordinate_system_transform=coordinate_system_transform,
|
||||
has_false_root_bone=psx_bone_create_result.has_false_root_bone,
|
||||
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))
|
||||
|
||||
@@ -299,7 +281,7 @@ 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,
|
||||
# has_false_root_bone=psx_bone_create_result.has_false_root_bone,
|
||||
)
|
||||
next_frame_bone_poses.append((location, rotation))
|
||||
|
||||
@@ -326,7 +308,7 @@ 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,
|
||||
# has_false_root_bone=psx_bone_create_result.has_false_root_bone,
|
||||
)
|
||||
add_key(location, rotation)
|
||||
|
||||
|
||||
@@ -238,7 +238,7 @@ def build_psk(context: Context, input_objects: PskInputObjects, options: PskBuil
|
||||
case _:
|
||||
assert False, f'Invalid export space: {options.export_space}'
|
||||
|
||||
vertex_transform_matrix = scale_matrix @ coordinate_system_matrix @ mesh_export_space_matrix
|
||||
vertex_transform_matrix = scale_matrix @ coordinate_system_matrix.inverted() @ mesh_export_space_matrix
|
||||
point_transform_matrix = vertex_transform_matrix @ mesh_object.matrix_world
|
||||
|
||||
# Vertices
|
||||
|
||||
@@ -172,18 +172,12 @@ def convert_string_to_cp1252_bytes(string: str) -> bytes:
|
||||
def create_psx_bones_from_blender_bones(
|
||||
bones: List[bpy.types.Bone],
|
||||
armature_object_matrix_world: Matrix,
|
||||
forward_axis: str = 'X',
|
||||
up_axis: str = 'Z',
|
||||
) -> List[PsxBone]:
|
||||
"""
|
||||
Creates PSX bones from the given Blender bones.
|
||||
|
||||
The bones are in world space based on the armature object's world matrix.
|
||||
"""
|
||||
|
||||
coordinate_system_transform = get_coordinate_system_transform(forward_axis, up_axis)
|
||||
coordinate_system_default_rotation = coordinate_system_transform.to_quaternion()
|
||||
|
||||
# Apply the scale of the armature object to the bone location.
|
||||
_, _, armature_object_scale = armature_object_matrix_world.decompose()
|
||||
|
||||
@@ -209,11 +203,9 @@ def create_psx_bones_from_blender_bones(
|
||||
location = (parent_tail - parent_head) + bone.head
|
||||
else:
|
||||
location = armature_object_matrix_world @ bone.head
|
||||
location = coordinate_system_transform @ location
|
||||
bone_rotation = bone.matrix.to_quaternion().conjugated()
|
||||
rotation = bone_rotation @ armature_object_matrix_world.to_3x3().to_quaternion()
|
||||
rotation.conjugate()
|
||||
rotation = coordinate_system_default_rotation @ rotation
|
||||
|
||||
location.x *= armature_object_scale.x
|
||||
location.y *= armature_object_scale.y
|
||||
@@ -352,9 +344,6 @@ def create_psx_bones(
|
||||
raise RuntimeError(f'When exporting multiple armatures, the Export Space must be World.\n' \
|
||||
f'The following armatures are attempting to be exported: {root_armature_names}')
|
||||
|
||||
coordinate_system_matrix = get_coordinate_system_transform(forward_axis, up_axis)
|
||||
coordinate_system_default_rotation = coordinate_system_matrix.to_quaternion()
|
||||
|
||||
total_bone_count = 0
|
||||
for armature_object in filter(lambda x: x.data is not None, armature_objects):
|
||||
armature_data = typing_cast(Armature, armature_object.data)
|
||||
@@ -384,7 +373,7 @@ def create_psx_bones(
|
||||
# requirement that a PSK file must have at least one bone.
|
||||
psx_bone = PsxBone()
|
||||
psx_bone.name = convert_string_to_cp1252_bytes(root_bone_name)
|
||||
psx_bone.rotation = convert_bpy_quaternion_to_psx_quaternion(coordinate_system_default_rotation)
|
||||
psx_bone.rotation = Quaternion(0.0, 0.0, 0.0, 1.0)
|
||||
bones.append((psx_bone, None))
|
||||
|
||||
armature_object_root_bone_indices[None] = 0
|
||||
@@ -394,7 +383,7 @@ def create_psx_bones(
|
||||
psx_bone = PsxBone()
|
||||
psx_bone.name = convert_string_to_cp1252_bytes(root_bone_name)
|
||||
psx_bone.children_count = total_bone_count
|
||||
psx_bone.rotation = convert_bpy_quaternion_to_psx_quaternion(coordinate_system_default_rotation)
|
||||
psx_bone.rotation = Quaternion(0.0, 0.0, 0.0, 1.0)
|
||||
bones.append((psx_bone, None))
|
||||
|
||||
armature_object_root_bone_indices[None] = 0
|
||||
@@ -408,8 +397,6 @@ def create_psx_bones(
|
||||
armature_psx_bones = create_psx_bones_from_blender_bones(
|
||||
bones=armature_bones,
|
||||
armature_object_matrix_world=armature_object.matrix_world,
|
||||
forward_axis=forward_axis,
|
||||
up_axis=up_axis,
|
||||
)
|
||||
|
||||
# We have the bones in world space. If we are attaching this armature to a parent bone, we need to convert
|
||||
@@ -541,6 +528,36 @@ def create_psx_bones(
|
||||
f''
|
||||
raise RuntimeError(error_message)
|
||||
|
||||
# Apply the scale to the bone locations.
|
||||
for psx_bone, _ in bones:
|
||||
psx_bone.location.x *= scale
|
||||
psx_bone.location.y *= scale
|
||||
psx_bone.location.z *= scale
|
||||
|
||||
coordinate_system_matrix = get_coordinate_system_transform(forward_axis, up_axis)
|
||||
coordinate_system_default_rotation = coordinate_system_matrix.to_quaternion()
|
||||
|
||||
# Apply the coordinate system transform to the root bone.
|
||||
root_psx_bone = bones[0][0]
|
||||
# Get transform matrix from root bone location and rotation.
|
||||
root_bone_location = Vector((root_psx_bone.location.x, root_psx_bone.location.y, root_psx_bone.location.z))
|
||||
root_bone_rotation = BpyQuaternion((root_psx_bone.rotation.w, root_psx_bone.rotation.x, root_psx_bone.rotation.y, root_psx_bone.rotation.z))
|
||||
root_bone_matrix = (
|
||||
Matrix.Translation(root_bone_location) @
|
||||
root_bone_rotation.to_matrix().to_4x4()
|
||||
)
|
||||
root_bone_matrix = coordinate_system_default_rotation.inverted().to_matrix().to_4x4() @ root_bone_matrix
|
||||
location, rotation, _ = root_bone_matrix.decompose()
|
||||
root_psx_bone.location.x = location.x
|
||||
root_psx_bone.location.y = location.y
|
||||
root_psx_bone.location.z = location.z
|
||||
root_psx_bone.rotation.w = rotation.w
|
||||
root_psx_bone.rotation.x = rotation.x
|
||||
root_psx_bone.rotation.y = rotation.y
|
||||
root_psx_bone.rotation.z = rotation.z
|
||||
|
||||
convert_bpy_quaternion_to_psx_quaternion(coordinate_system_default_rotation)
|
||||
|
||||
return PsxBoneCreateResult(
|
||||
bones=bones,
|
||||
armature_object_root_bone_indices=armature_object_root_bone_indices,
|
||||
|
||||
Reference in New Issue
Block a user