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:
parent
15e20cdefc
commit
5cb9714597
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
)
|
||||
|
Loading…
x
Reference in New Issue
Block a user