Skip to content

Commit

Permalink
Merge pull request #463 from HENDRIX-ZT2/develop
Browse files Browse the repository at this point in the history
Fixes for the animation system
  • Loading branch information
HENDRIX-ZT2 authored Sep 26, 2021
2 parents da5fdf5 + b1bbf24 commit 34514d3
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 36 deletions.
13 changes: 13 additions & 0 deletions io_scene_niftools/modules/nif_export/animation/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,10 @@ def create_controller(parent_block, target_name, priority=0):
elif isinstance(parent_block, NifFormat.NiControllerSequence):
controlled_block = parent_block.add_controlled_block()
controlled_block.priority = priority
# todo - pyffi adds the names to the NiStringPalette, but it creates one per controller link...
# also the currently used pyffi version doesn't store target_name for ZT2 style KFs in
# controlled_block.set_node_name(target_name)
# the following code handles both issues and should probably be ported to pyffi
if NifData.data.version < 0x0A020000:
# older versions need the actual controller blocks
controlled_block.target_name = target_name
Expand All @@ -157,6 +161,15 @@ def create_controller(parent_block, target_name, priority=0):
controlled_block.interpolator = n_kfi
controlled_block.node_name = target_name
controlled_block.controller_type = "NiTransformController"
# get the parent's string palette
if not parent_block.string_palette:
parent_block.string_palette = NifFormat.NiStringPalette()
# assign string palette to controller
controlled_block.string_palette = parent_block.string_palette
# add the strings and store their offsets
palette = controlled_block.string_palette.palette
controlled_block.node_name_offset = palette.add_string(controlled_block.node_name)
controlled_block.controller_type_offset = palette.add_string(controlled_block.controller_type)
else:
raise io_scene_niftools.utils.logging.NifError("Unsupported KeyframeController parent!")

Expand Down
30 changes: 7 additions & 23 deletions io_scene_niftools/modules/nif_export/animation/transform.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,6 @@ def export_kf_root(self, b_armature=None):
kf_root.stop_time = scene.frame_end / self.fps

kf_root.target_name = targetname
kf_root.string_palette = NifFormat.NiStringPalette()
else:
raise NifError(
f"Keyframe export for '{bpy.context.scene.niftools_scene.game}' is not supported.")
Expand Down Expand Up @@ -239,31 +238,16 @@ def export_transforms(self, parent_block, b_obj, b_action, bone=None):
scale_curve.append((frame, scale[0]))

if n_kfi:
if max(len(c) for c in (quat_curve, euler_curve, trans_curve, scale_curve)) > 1:
# number of frames is > 1, so add transform data
# set the default transforms of the interpolator as the bone's bind pose
n_kfi.translation.x, n_kfi.translation.y, n_kfi.translation.z = bind_trans
n_kfi.rotation.w, n_kfi.rotation.x, n_kfi.rotation.y, n_kfi.rotation.z = bind_rot.to_quaternion()
n_kfi.scale = bind_scale

if max(len(c) for c in (quat_curve, euler_curve, trans_curve, scale_curve)) > 0:
# number of frames is > 0, so add transform data
n_kfd = block_store.create_block("NiTransformData", exp_fcurves)
n_kfi.data = n_kfd
else:
# only add data if number of keys is > 1
# (see importer comments with import_kf_root: a single frame
# keyframe denotes an interpolator without further data)
# insufficient keys, so set the data and we're done!
if trans_curve:
trans = trans_curve[0][1]
n_kfi.translation.x, n_kfi.translation.y, n_kfi.translation.z = trans

if quat_curve:
quat = quat_curve[0][1]
elif euler_curve:
quat = euler_curve[0][1].to_quaternion()

if quat_curve or euler_curve:
n_kfi.rotation.x = quat.x
n_kfi.rotation.y = quat.y
n_kfi.rotation.z = quat.z
n_kfi.rotation.w = quat.w
# ignore scale for now...
n_kfi.scale = 1.0
# no need to add any keys, done
return

Expand Down
18 changes: 8 additions & 10 deletions io_scene_niftools/modules/nif_import/animation/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,12 @@

from io_scene_niftools.utils.logging import NifLog

FPS = 30


class Animation:

def __init__(self):
self.show_pose_markers()
self.fps = 30

@staticmethod
def show_pose_markers():
Expand Down Expand Up @@ -141,7 +140,7 @@ def add_key(self, fcurves, t, key, interp):
"""
Add a key (len=n) to a set of fcurves (len=n) at the given frame. Set the key's interpolation to interp.
"""
frame = round(t * animation.FPS)
frame = round(t * self.fps)
for fcurve, k in zip(fcurves, key):
fcurve.keyframe_points.insert(frame, k).interpolation = interp

Expand All @@ -159,13 +158,12 @@ def import_text_key_extra_data(self, txk, b_action):
if txk and b_action:
for key in txk.text_keys:
newkey = key.value.decode().replace('\r\n', '/').rstrip('/')
frame = round(key.time * animation.FPS)
frame = round(key.time * self.fps)
marker = b_action.pose_markers.new(newkey)
marker.frame = frame

@staticmethod
def set_frames_per_second(roots):
"""Scan all blocks and set a reasonable number for FPS to this class and the scene."""
def set_frames_per_second(self, roots):
"""Scan all blocks and set a reasonable number for fps to this class and the scene."""
# find all key times
key_times = []
for root in roots:
Expand Down Expand Up @@ -194,9 +192,9 @@ def set_frames_per_second(roots):
if not key_times:
return

# calculate FPS
# calculate fps
key_times = sorted(set(key_times))
fps = animation.FPS
fps = self.fps
lowest_diff = sum(abs(int(time * fps + 0.5) - (time * fps)) for time in key_times)

# for test_fps in range(1,120): #disabled, used for testing
Expand All @@ -206,6 +204,6 @@ def set_frames_per_second(roots):
lowest_diff = diff
fps = test_fps
NifLog.info(f"Animation estimated at {fps} frames per second.")
animation.FPS = fps
self.fps = fps
bpy.context.scene.render.fps = fps
bpy.context.scene.frame_set(0)
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ def import_sequence_stream_helper(self, kf_root, b_armature_obj, bind_data):

def import_controller_sequence(self, kf_root, b_armature_obj, bind_data):
NifLog.debug('Importing NiControllerSequence...')
b_action = self.create_action(b_armature_obj, kf_root.name.decode())
b_action = self.create_action(b_armature_obj, kf_root.name.decode(), retrieve=False)

# import text keys
self.import_text_keys(kf_root, b_action)
Expand Down
9 changes: 7 additions & 2 deletions io_scene_niftools/modules/nif_import/armature/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -235,15 +235,20 @@ def guess_orientation(self, n_armature):
# return string identifiers
return ids[forward_ind], ids[up_ind]

@staticmethod
def argmax(values):
"""Return the index of the max value in values"""
return max(zip(values, range(len(values))))[1]

def get_forward_axis(self, n_bone, axis_indices):
"""Helper function to get the forward axis of a bone"""
# check that n_block is indeed a bone
if not self.is_bone(n_bone):
return None
trans = n_bone.translation.as_tuple()
trans_abs = tuple(abs(v) for v in trans)
# do argmax
max_coord_ind = max(zip(trans_abs, range(len(trans_abs))))[1]
# get the index of the coordinate with the biggest absolute value
max_coord_ind = self.argmax(trans_abs)
# now check the sign
actual_value = trans[max_coord_ind]
# handle sign accordingly so negative indices map to the negative identifiers in list
Expand Down

0 comments on commit 34514d3

Please # to comment.