Initial working commit for full PSK/PSA import/export cycle.
This commit is contained in:
@@ -124,6 +124,6 @@ class PsaBuilder(object):
|
|||||||
sequence.bone_count = len(pose_bones)
|
sequence.bone_count = len(pose_bones)
|
||||||
sequence.track_time = frame_count
|
sequence.track_time = frame_count
|
||||||
|
|
||||||
psa.sequences.append(sequence)
|
psa.sequences[action.name] = sequence
|
||||||
|
|
||||||
return psa
|
return psa
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
from typing import List, Dict
|
import typing
|
||||||
|
from typing import List
|
||||||
|
from collections import OrderedDict
|
||||||
from ..data import *
|
from ..data import *
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@@ -47,4 +49,5 @@ class Psa(object):
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.bones: List[Psa.Bone] = []
|
self.bones: List[Psa.Bone] = []
|
||||||
self.sequences: Dict[Psa.Sequence] = {}
|
self.sequences: typing.OrderedDict[Psa.Sequence] = OrderedDict()
|
||||||
|
self.keys: List[Psa.Key] = []
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ class PsaExporter(object):
|
|||||||
with open(path, 'wb') as fp:
|
with open(path, 'wb') as fp:
|
||||||
self.write_section(fp, b'ANIMHEAD')
|
self.write_section(fp, b'ANIMHEAD')
|
||||||
self.write_section(fp, b'BONENAMES', Psa.Bone, self.psa.bones)
|
self.write_section(fp, b'BONENAMES', Psa.Bone, self.psa.bones)
|
||||||
self.write_section(fp, b'ANIMINFO', Psa.Sequence, self.psa.sequences)
|
self.write_section(fp, b'ANIMINFO', Psa.Sequence, list(self.psa.sequences.values()))
|
||||||
self.write_section(fp, b'ANIMKEYS', Psa.Key, self.psa.keys)
|
self.write_section(fp, b'ANIMKEYS', Psa.Key, self.psa.keys)
|
||||||
|
|
||||||
|
|
||||||
@@ -70,7 +70,7 @@ class PsaExportOperator(Operator, ExportHelper):
|
|||||||
box = layout.box()
|
box = layout.box()
|
||||||
box.label(text='Actions', icon='ACTION')
|
box.label(text='Actions', icon='ACTION')
|
||||||
row = box.row()
|
row = box.row()
|
||||||
row.template_list('PSA_UL_ActionList', 'asd', scene, 'psa_export.action_list', scene, 'psa_export.action_list_index', rows=len(context.scene.psa_export.action_list))
|
row.template_list('PSA_UL_ActionList', 'asd', scene.psa_export, 'action_list', scene.psa_export, 'action_list_index', rows=10)
|
||||||
|
|
||||||
def is_action_for_armature(self, action):
|
def is_action_for_armature(self, action):
|
||||||
if len(action.fcurves) == 0:
|
if len(action.fcurves) == 0:
|
||||||
|
|||||||
@@ -60,18 +60,33 @@ class PsaImporter(object):
|
|||||||
|
|
||||||
# Create intermediate bone data for import operations.
|
# Create intermediate bone data for import operations.
|
||||||
import_bones = []
|
import_bones = []
|
||||||
|
import_bones_dict = dict()
|
||||||
for psa_bone_index, psa_bone in enumerate(psa.bones):
|
for psa_bone_index, psa_bone in enumerate(psa.bones):
|
||||||
bone_name = psa_bone.name.decode('windows-1252')
|
bone_name = psa_bone.name.decode('windows-1252')
|
||||||
if psa_bone_index not in psa_to_armature_bone_indices:
|
print(bone_name)
|
||||||
|
if psa_bone_index not in psa_to_armature_bone_indices: # TODO: replace with bone_name in armature_data.bones
|
||||||
# PSA bone does not map to armature bone, skip it and leave an empty bone in its place.
|
# PSA bone does not map to armature bone, skip it and leave an empty bone in its place.
|
||||||
import_bones.append(None)
|
import_bones.append(None)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
import_bone = ImportBone(psa_bone)
|
import_bone = ImportBone(psa_bone)
|
||||||
armature_bone = armature_data.bones[bone_name]
|
armature_bone = armature_data.bones[bone_name]
|
||||||
import_bone.pose_bone = armature_object.pose.bones[bone_name]
|
import_bone.pose_bone = armature_object.pose.bones[bone_name]
|
||||||
if psa_bone_index > 0:
|
|
||||||
import_bone.parent = import_bones[psa_bone.parent_index]
|
if armature_bone.parent is not None:
|
||||||
|
if armature_bone.parent.name in psa_bone_names:
|
||||||
|
import_bone.parent = import_bones_dict[armature_bone.parent.name]
|
||||||
|
else:
|
||||||
|
import_bone.parent = None
|
||||||
|
|
||||||
# Calculate the original location & rotation of each bone (in world space maybe?)
|
# Calculate the original location & rotation of each bone (in world space maybe?)
|
||||||
|
# TODO: check if the armature bones have custom data written to them and use that instead.
|
||||||
|
if armature_bone.get('orig_quat') is not None:
|
||||||
|
# TODO: ideally we don't rely on bone auxiliary data like this, the non-aux data path is incorrect (animations are flipped 180)
|
||||||
|
import_bone.orig_quat = Quaternion(armature_bone['orig_quat'])
|
||||||
|
import_bone.orig_loc = Vector(armature_bone['orig_loc'])
|
||||||
|
import_bone.post_quat = Quaternion(armature_bone['post_quat'])
|
||||||
|
else:
|
||||||
if import_bone.parent is not None:
|
if import_bone.parent is not None:
|
||||||
import_bone.orig_loc = armature_bone.matrix_local.translation - armature_bone.parent.matrix_local.translation
|
import_bone.orig_loc = armature_bone.matrix_local.translation - armature_bone.parent.matrix_local.translation
|
||||||
import_bone.orig_loc.rotate(armature_bone.parent.matrix_local.to_quaternion().conjugated())
|
import_bone.orig_loc.rotate(armature_bone.parent.matrix_local.to_quaternion().conjugated())
|
||||||
@@ -82,6 +97,7 @@ class PsaImporter(object):
|
|||||||
import_bone.orig_loc = armature_bone.matrix_local.translation.copy()
|
import_bone.orig_loc = armature_bone.matrix_local.translation.copy()
|
||||||
import_bone.orig_quat = armature_bone.matrix_local.to_quaternion()
|
import_bone.orig_quat = armature_bone.matrix_local.to_quaternion()
|
||||||
import_bone.post_quat = import_bone.orig_quat.conjugated()
|
import_bone.post_quat = import_bone.orig_quat.conjugated()
|
||||||
|
import_bones_dict[bone_name] = import_bone
|
||||||
import_bones.append(import_bone)
|
import_bones.append(import_bone)
|
||||||
|
|
||||||
# Create and populate the data for new sequences.
|
# Create and populate the data for new sequences.
|
||||||
@@ -114,9 +130,7 @@ class PsaImporter(object):
|
|||||||
import_bone.fcurve_location_y.keyframe_points.add(sequence.frame_count)
|
import_bone.fcurve_location_y.keyframe_points.add(sequence.frame_count)
|
||||||
import_bone.fcurve_location_z.keyframe_points.add(sequence.frame_count)
|
import_bone.fcurve_location_z.keyframe_points.add(sequence.frame_count)
|
||||||
|
|
||||||
fcurve_interpolation = 'LINEAR'
|
|
||||||
should_invert_root = False
|
should_invert_root = False
|
||||||
|
|
||||||
key_index = 0
|
key_index = 0
|
||||||
sequence_name = sequence.name.decode('windows-1252')
|
sequence_name = sequence.name.decode('windows-1252')
|
||||||
sequence_keys = psa_reader.get_sequence_keys(sequence_name)
|
sequence_keys = psa_reader.get_sequence_keys(sequence_name)
|
||||||
@@ -131,13 +145,12 @@ class PsaImporter(object):
|
|||||||
key_location = Vector(tuple(sequence_keys[key_index].location))
|
key_location = Vector(tuple(sequence_keys[key_index].location))
|
||||||
key_rotation = Quaternion(tuple(sequence_keys[key_index].rotation))
|
key_rotation = Quaternion(tuple(sequence_keys[key_index].rotation))
|
||||||
|
|
||||||
# TODO: what is this doing exactly?
|
|
||||||
q = import_bone.post_quat.copy()
|
q = import_bone.post_quat.copy()
|
||||||
q.rotate(import_bone.orig_quat)
|
q.rotate(import_bone.orig_quat)
|
||||||
quat = q
|
quat = q
|
||||||
q = import_bone.post_quat.copy()
|
q = import_bone.post_quat.copy()
|
||||||
if import_bone.parent is None and should_invert_root:
|
if import_bone.parent is None and not should_invert_root:
|
||||||
q.rotate(import_bone.orig_quat)
|
q.rotate(key_rotation.conjugated())
|
||||||
else:
|
else:
|
||||||
q.rotate(key_rotation)
|
q.rotate(key_rotation)
|
||||||
quat.rotate(q.conjugated())
|
quat.rotate(q.conjugated())
|
||||||
@@ -153,14 +166,6 @@ class PsaImporter(object):
|
|||||||
import_bone.fcurve_location_y.keyframe_points[frame_index].co = frame_index, loc.y
|
import_bone.fcurve_location_y.keyframe_points[frame_index].co = frame_index, loc.y
|
||||||
import_bone.fcurve_location_z.keyframe_points[frame_index].co = frame_index, loc.z
|
import_bone.fcurve_location_z.keyframe_points[frame_index].co = frame_index, loc.z
|
||||||
|
|
||||||
import_bone.fcurve_quat_w.keyframe_points[frame_index].interpolation = fcurve_interpolation
|
|
||||||
import_bone.fcurve_quat_x.keyframe_points[frame_index].interpolation = fcurve_interpolation
|
|
||||||
import_bone.fcurve_quat_y.keyframe_points[frame_index].interpolation = fcurve_interpolation
|
|
||||||
import_bone.fcurve_quat_z.keyframe_points[frame_index].interpolation = fcurve_interpolation
|
|
||||||
import_bone.fcurve_location_x.keyframe_points[frame_index].interpolation = fcurve_interpolation
|
|
||||||
import_bone.fcurve_location_z.keyframe_points[frame_index].interpolation = fcurve_interpolation
|
|
||||||
import_bone.fcurve_location_z.keyframe_points[frame_index].interpolation = fcurve_interpolation
|
|
||||||
|
|
||||||
key_index += 1
|
key_index += 1
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -43,6 +43,9 @@ class PskImporter(object):
|
|||||||
self.world_rotation_matrix: Matrix = Matrix()
|
self.world_rotation_matrix: Matrix = Matrix()
|
||||||
self.world_matrix: Matrix = Matrix()
|
self.world_matrix: Matrix = Matrix()
|
||||||
self.vertex_group = None
|
self.vertex_group = None
|
||||||
|
self.orig_quat: Quaternion = Quaternion()
|
||||||
|
self.orig_loc: Vector = Vector()
|
||||||
|
self.post_quat: Quaternion = Quaternion()
|
||||||
|
|
||||||
import_bones = []
|
import_bones = []
|
||||||
should_invert_root = False
|
should_invert_root = False
|
||||||
@@ -73,19 +76,28 @@ class PskImporter(object):
|
|||||||
bone.world_rotation_matrix = bone.local_rotation.conjugated().to_matrix()
|
bone.world_rotation_matrix = bone.local_rotation.conjugated().to_matrix()
|
||||||
bone.world_rotation_matrix.rotate(parent.world_rotation_matrix)
|
bone.world_rotation_matrix.rotate(parent.world_rotation_matrix)
|
||||||
|
|
||||||
for bone in import_bones:
|
for import_bone in import_bones:
|
||||||
edit_bone = armature_data.edit_bones.new(bone.psk_bone.name.decode('utf-8'))
|
bone_name = import_bone.psk_bone.name.decode('utf-8')
|
||||||
if bone.parent is not None:
|
edit_bone = armature_data.edit_bones.new(bone_name)
|
||||||
edit_bone.parent = armature_data.edit_bones[bone.psk_bone.parent_index]
|
|
||||||
|
if import_bone.parent is not None:
|
||||||
|
edit_bone.parent = armature_data.edit_bones[import_bone.psk_bone.parent_index]
|
||||||
elif not should_invert_root:
|
elif not should_invert_root:
|
||||||
bone.local_rotation.conjugate()
|
import_bone.local_rotation.conjugate()
|
||||||
|
|
||||||
edit_bone.tail = Vector((0.0, new_bone_size, 0.0))
|
edit_bone.tail = Vector((0.0, new_bone_size, 0.0))
|
||||||
edit_bone_matrix = bone.local_rotation.conjugated()
|
edit_bone_matrix = import_bone.local_rotation.conjugated()
|
||||||
edit_bone_matrix.rotate(bone.world_matrix)
|
edit_bone_matrix.rotate(import_bone.world_matrix)
|
||||||
edit_bone_matrix = edit_bone_matrix.to_matrix().to_4x4()
|
edit_bone_matrix = edit_bone_matrix.to_matrix().to_4x4()
|
||||||
edit_bone_matrix.translation = bone.world_matrix.translation
|
edit_bone_matrix.translation = import_bone.world_matrix.translation
|
||||||
edit_bone.matrix = edit_bone_matrix
|
edit_bone.matrix = edit_bone_matrix
|
||||||
|
|
||||||
|
# Store bind pose information in the bone's custom properties.
|
||||||
|
# This information is used when importing animations from PSA files.
|
||||||
|
edit_bone['orig_quat'] = import_bone.local_rotation
|
||||||
|
edit_bone['orig_loc'] = import_bone.local_translation
|
||||||
|
edit_bone['post_quat'] = import_bone.local_rotation.conjugated()
|
||||||
|
|
||||||
# MESH
|
# MESH
|
||||||
mesh_data = bpy.data.meshes.new(name)
|
mesh_data = bpy.data.meshes.new(name)
|
||||||
mesh_object = bpy.data.objects.new(name, mesh_data)
|
mesh_object = bpy.data.objects.new(name, mesh_data)
|
||||||
|
|||||||
Reference in New Issue
Block a user