1
0
mirror of https://github.com/DarklightGames/io_scene_psk_psa.git synced 2025-03-03 23:32:35 +01:00

Added an option to bake imported PSA sequences to samples.

Also, the "Clean Keyframes" option has been removed from the PSA import
because it did not work correctly.

This will be replaced by a "Decimate F-Curves" option in the future.
This commit is contained in:
Colin Basnett 2022-11-01 11:33:39 -07:00
parent 4181a15d0e
commit 02082b9594

View File

@ -4,7 +4,6 @@ import re
from typing import List, Optional
import bpy
import numpy as np
from bpy.props import StringProperty, BoolProperty, CollectionProperty, PointerProperty, IntProperty
from bpy.types import Operator, UIList, PropertyGroup, Panel
from bpy_extras.io_utils import ImportHelper
@ -16,7 +15,6 @@ from .reader import PsaReader
class PsaImportOptions(object):
def __init__(self):
self.should_clean_keys = True
self.should_use_fake_user = False
self.should_stash = False
self.sequence_names = []
@ -24,6 +22,7 @@ class PsaImportOptions(object):
self.should_write_keyframes = True
self.should_write_metadata = True
self.action_name_prefix = ''
self.should_convert_to_samples = False
class ImportBone(object):
@ -154,7 +153,6 @@ def import_psa(psa_reader: PsaReader, armature_object, options: PsaImportOptions
# Read the sequence data matrix from the PSA.
sequence_data_matrix = psa_reader.read_sequence_data_matrix(sequence_name)
keyframe_write_matrix = np.ones(sequence_data_matrix.shape, dtype=np.int8)
# Convert the sequence's data from world-space to local-space.
for bone_index, import_bone in enumerate(import_bones):
@ -166,40 +164,19 @@ def import_psa(psa_reader: PsaReader, armature_object, options: PsaImportOptions
# Calculate the local-space key data for the bone.
sequence_data_matrix[frame_index, bone_index] = calculate_fcurve_data(import_bone, key_data)
# Clean the keyframe data. This is accomplished by writing zeroes to the write matrix when there is an
# insufficiently large change in the data from the last written frame.
if options.should_clean_keys:
threshold = 0.001
for bone_index, import_bone in enumerate(import_bones):
if import_bone is None:
continue
for fcurve_index in range(len(import_bone.fcurves)):
# Get all the keyframe data for the bone's f-curve data from the sequence data matrix.
fcurve_frame_data = sequence_data_matrix[:, bone_index, fcurve_index]
last_written_datum = 0
for frame_index, datum in enumerate(fcurve_frame_data):
# If the f-curve data is not different enough to the last written frame,
# un-mark this data for writing.
if frame_index > 0 and abs(datum - last_written_datum) < threshold:
keyframe_write_matrix[frame_index, bone_index, fcurve_index] = 0
else:
last_written_datum = datum
# Write the keyframes out!
for frame_index in range(sequence.frame_count):
for bone_index, import_bone in enumerate(import_bones):
if import_bone is None:
continue
bone_has_writeable_keyframes = any(keyframe_write_matrix[frame_index, bone_index])
if bone_has_writeable_keyframes:
# This bone has writeable keyframes for this frame.
key_data = sequence_data_matrix[frame_index, bone_index]
for fcurve, should_write, datum in zip(import_bone.fcurves,
keyframe_write_matrix[frame_index, bone_index],
key_data):
if should_write:
for fcurve, datum in zip(import_bone.fcurves, key_data):
fcurve.keyframe_points.insert(frame_index, datum, options={'FAST'})
if options.should_convert_to_samples:
for fcurve in action.fcurves:
fcurve.convert_to_samples(start=0, end=sequence.frame_count)
# Write
if options.should_write_metadata:
action['psa_sequence_name'] = sequence_name
@ -266,9 +243,6 @@ class PsaImportPropertyGroup(PropertyGroup):
psa: PointerProperty(type=PsaDataPropertyGroup)
sequence_list: CollectionProperty(type=PsaImportActionListItem)
sequence_list_index: IntProperty(name='', default=0)
should_clean_keys: BoolProperty(default=True, name='Clean Keyframes',
description='Exclude unnecessary keyframes from being written to the actions',
options=empty_set)
should_use_fake_user: BoolProperty(default=True, name='Fake User',
description='Assign each imported action a fake user so that the data block is saved even it has no users',
options=empty_set)
@ -289,6 +263,11 @@ class PsaImportPropertyGroup(PropertyGroup):
sequence_use_filter_regex: BoolProperty(default=False, name='Regular Expression',
description='Filter using regular expressions', options=empty_set)
select_text: PointerProperty(type=bpy.types.Text)
should_convert_to_samples: BoolProperty(
default=True,
name='Convert to Samples',
description='Convert keyframes to read-only samples. Recommended if you do not plan on editing the actions directly'
)
def filter_sequences(pg: PsaImportPropertyGroup, sequences) -> List[int]:
@ -457,11 +436,14 @@ class PSA_PT_ImportPanel_Advanced(Panel):
layout = self.layout
pg = getattr(context.scene, 'psa_import')
col = layout.column(heading="Options")
col = layout.column(heading='Keyframes')
col.use_property_split = True
col.use_property_decorate = False
col.prop(pg, 'should_clean_keys')
col.prop(pg, 'should_convert_to_samples')
col.separator()
col = layout.column(heading='Options')
col.use_property_split = True
col.use_property_decorate = False
col.prop(pg, 'should_use_fake_user')
col.prop(pg, 'should_stash')
col.prop(pg, 'should_use_action_name_prefix')
@ -585,13 +567,13 @@ class PsaImportOperator(Operator):
options = PsaImportOptions()
options.sequence_names = sequence_names
options.should_clean_keys = pg.should_clean_keys
options.should_use_fake_user = pg.should_use_fake_user
options.should_stash = pg.should_stash
options.action_name_prefix = pg.action_name_prefix if pg.should_use_action_name_prefix else ''
options.should_overwrite = pg.should_overwrite
options.should_write_metadata = pg.should_write_metadata
options.should_write_keyframes = pg.should_write_keyframes
options.should_convert_to_samples = pg.should_convert_to_samples
import_psa(psa_reader, context.view_layer.objects.active, options)