1
0
mirror of https://github.com/DarklightGames/io_scene_psk_psa.git synced 2025-02-21 11:39:37 +01:00

Added key compression functionality

This commit is contained in:
Colin Basnett 2023-04-30 20:18:59 -07:00
parent 15e20cdefc
commit 5cb9714597
4 changed files with 83 additions and 33 deletions

View File

@ -103,12 +103,14 @@ def register():
bpy.types.Scene.psa_import = PointerProperty(type=psa_importer.PsaImportPropertyGroup)
bpy.types.Scene.psa_export = PointerProperty(type=psa_exporter.PsaExportPropertyGroup)
bpy.types.Scene.psk_export = PointerProperty(type=psk_exporter.PskExportPropertyGroup)
bpy.types.Action.psa_export = PointerProperty(type=psx_types.PSX_PG_ActionExportPropertyGroup)
def unregister():
del bpy.types.Scene.psa_import
del bpy.types.Scene.psa_export
del bpy.types.Scene.psk_export
del bpy.types.Action.psa_export
bpy.types.TOPBAR_MT_file_export.remove(psk_export_menu_func)
bpy.types.TOPBAR_MT_file_import.remove(psk_import_menu_func)
bpy.types.TOPBAR_MT_file_export.remove(psa_export_menu_func)

View File

@ -1,6 +1,6 @@
from typing import Optional
from bpy.types import Armature, Bone, Action
from bpy.types import Armature, Bone, Action, PoseBone
from .data import *
from ..helpers import *
@ -16,6 +16,8 @@ class PsaExportSequence:
def __init__(self):
self.name: str = ''
self.nla_state: PsaExportSequence.NlaState = PsaExportSequence.NlaState()
self.compression_ratio: float = 1.0
self.key_quota: int = 0
self.fps: float = 30.0
@ -31,6 +33,28 @@ class PsaBuildOptions:
self.root_motion: bool = False
def get_pose_bone_location_and_rotation(pose_bone: PoseBone, armature_object: Object, options: PsaBuildOptions):
if 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
else:
if options.root_motion:
# Get the bone's pose matrix, taking the armature object's world matrix into account.
pose_bone_matrix = armature_object.matrix_world @ pose_bone.matrix
else:
# Use the bind pose matrix for the root bone.
pose_bone_matrix = pose_bone.matrix
location = pose_bone_matrix.to_translation()
rotation = pose_bone_matrix.to_quaternion().normalized()
if pose_bone.parent is not None:
rotation.conjugate()
return location, rotation
def build_psa(context: bpy.types.Context, options: PsaBuildOptions) -> Psa:
active_object = context.view_layer.objects.active
@ -121,42 +145,40 @@ def build_psa(context: bpy.types.Context, options: PsaBuildOptions) -> Psa:
frame_start = export_sequence.nla_state.frame_start
frame_end = export_sequence.nla_state.frame_end
frame_count = abs(frame_end - frame_start) + 1
frame_step = 1 if frame_start < frame_end else -1
# Calculate the frame step based on the compression factor.
frame_extents = abs(frame_end - frame_start)
frame_count_raw = frame_extents + 1
frame_count = max(export_sequence.key_quota, int(frame_count_raw * export_sequence.compression_ratio))
try:
frame_step = frame_extents / (frame_count - 1)
except ZeroDivisionError:
frame_step = 0.0
sequence_duration = frame_count_raw / export_sequence.fps
# If this is a reverse sequence, we need to reverse the frame step.
if frame_start > frame_end:
frame_step = -frame_step
psa_sequence = Psa.Sequence()
psa_sequence.name = bytes(export_sequence.name, encoding='windows-1252')
psa_sequence.frame_count = frame_count
psa_sequence.frame_start_index = frame_start_index
psa_sequence.fps = export_sequence.fps
psa_sequence.fps = frame_count / sequence_duration
psa_sequence.bone_count = len(pose_bones)
psa_sequence.track_time = frame_count
frame = float(frame_start)
frame = frame_start
for _ in range(frame_count):
context.scene.frame_set(frame)
frame += frame_step
context.scene.frame_set(frame=int(frame), subframe=frame % 1.0)
for pose_bone in pose_bones:
location, rotation = get_pose_bone_location_and_rotation(pose_bone, armature_object, options)
key = Psa.Key()
if 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
else:
if options.root_motion:
# Get the bone's pose matrix, taking the armature object's world matrix into account.
pose_bone_matrix = armature_object.matrix_world @ pose_bone.matrix
else:
# Use the bind pose matrix for the root bone.
pose_bone_matrix = armature_data.bones[pose_bone.name].matrix_local
location = pose_bone_matrix.to_translation()
rotation = pose_bone_matrix.to_quaternion().normalized()
if pose_bone.parent is not None:
rotation.conjugate()
key.location.x = location.x
key.location.y = location.y
key.location.z = location.z
@ -165,11 +187,9 @@ def build_psa(context: bpy.types.Context, options: PsaBuildOptions) -> Psa:
key.rotation.z = rotation.z
key.rotation.w = rotation.w
key.time = 1.0 / psa_sequence.fps
psa.keys.append(key)
psa_sequence.bone_count = len(pose_bones)
psa_sequence.track_time = frame_count
frame += frame_step
frame_start_index += frame_count

View File

@ -500,6 +500,8 @@ class PsaExportOperator(Operator, ExportHelper):
export_sequence.nla_state.frame_start = action.frame_start
export_sequence.nla_state.frame_end = action.frame_end
export_sequence.fps = get_sequence_fps(context, pg.fps_source, pg.fps_custom, [action.action])
export_sequence.compression_ratio = action.action.psa_export.compression_ratio
export_sequence.key_quota = action.action.psa_export.key_quota
export_sequences.append(export_sequence)
elif pg.sequence_source == 'TIMELINE_MARKERS':
for marker in pg.marker_list:

View File

@ -1,6 +1,6 @@
import bpy.props
from bpy.props import StringProperty, IntProperty, BoolProperty
from bpy.types import PropertyGroup, UIList, UILayout, Context, AnyType, Operator
from bpy.props import StringProperty, IntProperty, BoolProperty, FloatProperty
from bpy.types import PropertyGroup, UIList, UILayout, Context, AnyType, Operator, Panel
class PSX_UL_BoneGroupList(UIList):
@ -69,10 +69,36 @@ class BoneGroupListItem(PropertyGroup):
is_selected: BoolProperty(default=False)
class PSX_PG_ActionExportPropertyGroup(PropertyGroup):
compression_ratio: FloatProperty(name='Compression Ratio', default=1.0, min=0.0, max=1.0, subtype='FACTOR', description='The ratio of frames to be exported.\n\nA compression ratio of 1.0 will export all frames, while a compression ratio of 0.5 will export half of the frames')
key_quota: IntProperty(name='Key Quota', default=0, min=1, description='The minimum number of frames to be exported')
class PSX_PT_ActionPropertyPanel(Panel):
bl_idname = 'PSX_PT_ActionPropertyPanel'
bl_label = 'PSA Export'
bl_space_type = 'DOPESHEET_EDITOR'
bl_region_type = 'UI'
bl_context = 'action'
bl_category = 'Action'
@classmethod
def poll(cls, context: 'Context'):
return context.active_object and context.active_object.type == 'ARMATURE' and context.active_action is not None
def draw(self, context: 'Context'):
action = context.active_action
layout = self.layout
layout.prop(action.psa_export, 'compression_ratio')
layout.prop(action.psa_export, 'key_quota')
classes = (
PSX_PG_ActionExportPropertyGroup,
BoneGroupListItem,
PSX_UL_BoneGroupList,
PSX_UL_MaterialPathList,
PSX_OT_MaterialPathAdd,
PSX_OT_MaterialPathRemove
PSX_OT_MaterialPathRemove,
PSX_PT_ActionPropertyPanel
)