Browse Source

Animations: allow baking keyframes

pull/436/head
Jrius 2 months ago
parent
commit
c3ac168b94
  1. 40
      korman/exporter/animation.py
  2. 3
      korman/properties/modifiers/anim.py
  3. 23
      korman/properties/prop_anim.py
  4. 5
      korman/ui/ui_anim.py

40
korman/exporter/animation.py

@ -26,6 +26,7 @@ import weakref
from PyHSPlasma import *
from . import utils
from ..helpers import GoodNeighbor
class AnimationConverter:
def __init__(self, exporter):
@ -36,19 +37,45 @@ class AnimationConverter:
return frame_num / self._bl_fps
def convert_object_animations(self, bo: bpy.types.Object, so: plSceneObject, anim_name: str, *,
start: Optional[int] = None, end: Optional[int] = None) -> Iterable[plAGApplicator]:
start: Optional[int] = None, end: Optional[int] = None, bake_frame_step: Optional[int] = None) -> Iterable[plAGApplicator]:
if not bo.plasma_object.has_animation_data:
return []
def fetch_animation_data(id_data):
temporary_actions = []
def bake_animation_data():
# Baking animations is a Blender operator, so requires a bit of boilerplate...
with GoodNeighbor() as toggle:
# Make sure we have only this object selected.
toggle.track(bo, "hide", False)
for i in bpy.data.objects:
i.select = i == bo
bpy.context.scene.objects.active = bo
# Do bake, but make sure we don't mess the user's data.
old_action = bo.animation_data.action
try:
frame_start = start if start is not None else bpy.context.scene.frame_start
frame_end = end if end is not None else bpy.context.scene.frame_end
bpy.ops.nla.bake(frame_start=frame_start, frame_end=frame_end, step=bake_frame_step, visual_keying=True, bake_types={"POSE", "OBJECT"})
action = bo.animation_data.action
finally:
bo.animation_data.action = old_action
temporary_actions.append(action)
return action
def fetch_animation_data(id_data, can_bake):
if id_data is not None:
if id_data.animation_data is not None:
action = id_data.animation_data.action
if bake_frame_step is not None and can_bake:
action = bake_animation_data()
return action, getattr(action, "fcurves", [])
return None, []
obj_action, obj_fcurves = fetch_animation_data(bo)
data_action, data_fcurves = fetch_animation_data(bo.data)
try:
obj_action, obj_fcurves = fetch_animation_data(bo, True)
data_action, data_fcurves = fetch_animation_data(bo.data, False)
# We're basically just going to throw all the FCurves at the controller converter (read: wall)
# and see what sticks. PlasmaMAX has some nice animation channel stuff that allows for some
@ -69,6 +96,11 @@ class AnimationConverter:
if isinstance(lamp, bpy.types.PointLamp):
applicators.extend(self._convert_omni_lamp_animation(bo.name, data_fcurves, lamp, start, end))
finally:
for action in temporary_actions:
# Baking data is temporary, but the lifetime of our user's data is eternal !
bpy.data.actions.remove(action)
return [i for i in applicators if i is not None]
def _convert_camera_animation(self, bo, so, obj_fcurves, data_fcurves, anim_name: str,

3
korman/properties/modifiers/anim.py

@ -99,8 +99,9 @@ class PlasmaAnimationModifier(ActionModifier, PlasmaModifierProperties):
start, end = min((start, end)), max((start, end))
else:
start, end = None, None
bake_frame_step = anim.bake_frame_step if anim.bake else None
applicators = converter.convert_object_animations(bo, so, anim_name, start=start, end=end)
applicators = converter.convert_object_animations(bo, so, anim_name, start=start, end=end, bake_frame_step=bake_frame_step)
if not applicators:
exporter.report.warn(f"Animation '{anim_name}' generated no applicators. Nothing will be exported.")
continue

23
korman/properties/prop_anim.py

@ -127,6 +127,29 @@ class PlasmaAnimation(bpy.types.PropertyGroup):
bpy.types.Texture: "plasma_layer.anim_loop_end",
},
},
"bake": {
"type": BoolProperty,
"property": {
"name": "Bake Keyframes",
"description": "Bake animation keyframes on export. This generates a lot more intermediary keyframes but allows exporting inverse kinematics, and may improve timing for complex animations",
"default": False,
},
"entire_animation": {
bpy.types.Object: "plasma_modifiers.animation.bake",
},
},
"bake_frame_step": {
"type": IntProperty,
"property": {
"name": "Frame step",
"description": "How many frames between each keyframe sample",
"default": 1,
"min": 1,
},
"entire_animation": {
bpy.types.Object: "plasma_modifiers.animation.bake_frame_step",
},
},
"sdl_var": {
"type": StringProperty,
"property": {

5
korman/ui/ui_anim.py

@ -67,6 +67,11 @@ def draw_single_animation(layout, anim):
col.active = anim.loop and not anim.sdl_var
col.prop_search(anim, "loop_start", action, "pose_markers", icon="PMARKER")
col.prop_search(anim, "loop_end", action, "pose_markers", icon="PMARKER")
layout.separator()
split = layout.split()
split.prop(anim, "bake")
if anim.bake:
split.prop(anim, "bake_frame_step")
layout.separator()
layout.prop(anim, "sdl_var")

Loading…
Cancel
Save