feat: action refactoring

This commit is contained in:
Swann
2020-03-27 15:50:25 +01:00
parent 98d86c050b
commit ef9e9dbae8

View File

@ -19,11 +19,179 @@
import bpy
import mathutils
import copy
import logging
import numpy as np
from enum import Enum
from .. import utils
from .bl_datablock import BlDatablock
# WIP
logger = logging.getLogger(__name__)
ENUM_EASING_TYPE = [
'AUTO',
'EAS_IN',
'EASE_OUT',
'EASE_IN_OUT']
ENUM_HANDLE_TYPE = [
'FREE',
'ALIGNED',
'VECTOR',
'AUTO',
'AUTO_CLAMPED']
ENUM_INTERPOLATION_TYPE = [
'CONSTANT',
'LINEAR',
'BEZIER',
'SINE',
'QUAD',
'CUBIC',
'QUART',
'QUINT',
'EXPO',
'CIRC',
'BACK',
'BOUNCE',
'ELASTIC']
ENUM_KEY_TYPE = [
'KEYFRAME',
'BREAKDOWN',
'MOVING_HOLD',
'EXTREME',
'JITTER']
#TODO: Automatic enum and numpy dump and loading
def dump_fcurve(fcurve, use_numpy=True):
""" Dump a sigle curve to a dict
:arg fcurve: fcurve to dump
:type fcurve: bpy.types.FCurve
:arg use_numpy: use numpy to eccelerate dump
:type use_numpy: bool
:return: dict
"""
fcurve_data = {
"data_path": fcurve.data_path,
"dumped_array_index": fcurve.array_index,
"use_numpy": use_numpy
}
if use_numpy:
keyframes_count = len(fcurve.keyframe_points)
k_amplitude = np.empty(keyframes_count, dtype=np.float64)
fcurve.keyframe_points.foreach_get('amplitude', k_amplitude)
k_co = np.empty(keyframes_count*2, dtype=np.float64)
fcurve.keyframe_points.foreach_get('co', k_co)
k_back = np.empty(keyframes_count, dtype=np.float64)
fcurve.keyframe_points.foreach_get('back', k_back)
k_handle_left = np.empty(keyframes_count*2, dtype=np.float64)
fcurve.keyframe_points.foreach_get('handle_left', k_handle_left)
k_handle_right = np.empty(keyframes_count*2, dtype=np.float64)
fcurve.keyframe_points.foreach_get('handle_right', k_handle_right)
fcurve_data['amplitude'] = k_amplitude.tobytes()
fcurve_data['co'] = k_co.tobytes()
fcurve_data['back'] = k_back.tobytes()
fcurve_data['handle_left'] = k_handle_left.tobytes()
fcurve_data['handle_right'] = k_handle_right.tobytes()
fcurve_data['easing'] = [ENUM_EASING_TYPE.index(p.easing) for p in fcurve.keyframe_points]
fcurve_data['handle_left_type'] = [ENUM_HANDLE_TYPE.index(p.handle_left_type) for p in fcurve.keyframe_points]
fcurve_data['handle_right_type'] = [ENUM_HANDLE_TYPE.index(p.handle_right_type) for p in fcurve.keyframe_points]
fcurve_data['type'] = [ENUM_KEY_TYPE.index(p.type) for p in fcurve.keyframe_points]
fcurve_data['interpolation'] = [ENUM_INTERPOLATION_TYPE.index(p.interpolation) for p in fcurve.keyframe_points]
else: # Legacy method
dumper = utils.dump_anything.Dumper()
fcurve_data["keyframe_points"] = []
for k in fcurve.keyframe_points:
fcurve_data["keyframe_points"].append(
dumper.dump(k)
)
return fcurve_data
def load_fcurve(fcurve_data, fcurve):
""" Load a dumped fcurve
:arg fcurve_data: a dumped fcurve
:type fcurve_data: dict
:arg fcurve: fcurve to dump
:type fcurve: bpy.types.FCurve
"""
use_numpy = fcurve_data.get('use_numpy')
keyframe_points = fcurve.keyframe_points
# Remove all keyframe points
for i in range(len(keyframe_points)):
keyframe_points.remove(keyframe_points[0], fast=True)
if use_numpy:
k_amplitude = np.frombuffer(fcurve_data['amplitude'], dtype=np.float64)
keyframe_count = len(k_amplitude)
k_co = np.frombuffer(fcurve_data['co'], dtype=np.float64)
k_back = np.frombuffer(fcurve_data['back'], dtype=np.float64)
k_amplitude = np.frombuffer(fcurve_data['amplitude'], dtype=np.float64)
k_handle_left= np.frombuffer(fcurve_data['handle_left'], dtype=np.float64)
k_handle_right= np.frombuffer(fcurve_data['handle_right'], dtype=np.float64)
keyframe_points.add(keyframe_count)
keyframe_points.foreach_set('co',k_co)
keyframe_points.foreach_set('back',k_back)
keyframe_points.foreach_set('amplitude',k_amplitude)
keyframe_points.foreach_set('handle_left',k_handle_left)
keyframe_points.foreach_set('handle_right',k_handle_right)
for index, point in enumerate(keyframe_points):
point.type = ENUM_KEY_TYPE[fcurve_data['type'][index]]
point.easing = ENUM_EASING_TYPE[fcurve_data['easing'][index]]
point.handle_left_type = ENUM_HANDLE_TYPE[fcurve_data['handle_left_type'][index]]
point.handle_right_type = ENUM_HANDLE_TYPE[fcurve_data['handle_right_type'][index]]
point.interpolation = ENUM_INTERPOLATION_TYPE[fcurve_data['interpolation'][index]]
else:
# paste dumped keyframes
for dumped_keyframe_point in fcurve_data["keyframe_points"]:
if dumped_keyframe_point['type'] == '':
dumped_keyframe_point['type'] = 'KEYFRAME'
new_kf = keyframe_points.insert(
dumped_keyframe_point["co"][0],
dumped_keyframe_point["co"][1],
options={'FAST', 'REPLACE'}
)
keycache = copy.copy(dumped_keyframe_point)
keycache = utils.dump_anything.remove_items_from_dict(
keycache,
["co", "handle_left", "handle_right", 'type']
)
utils.dump_anything.load(new_kf, keycache)
new_kf.type = dumped_keyframe_point['type']
new_kf.handle_left = [
dumped_keyframe_point["handle_left"][0],
dumped_keyframe_point["handle_left"][1]
]
new_kf.handle_right = [
dumped_keyframe_point["handle_right"][0],
dumped_keyframe_point["handle_right"][1]
]
fcurve.update()
class BlAction(BlDatablock):
bl_id = "actions"
@ -32,30 +200,11 @@ class BlAction(BlDatablock):
bl_delay_apply = 1
bl_automatic_push = True
bl_icon = 'ACTION_TWEAK'
def _construct(self, data):
return bpy.data.actions.new(data["name"])
def _load(self, data, target):
begin_frame = 100000
end_frame = -100000
for dumped_fcurve in data["fcurves"]:
begin_frame = min(
begin_frame,
min(
[begin_frame] + [dkp["co"][0] for dkp in dumped_fcurve["keyframe_points"]]
)
)
end_frame = max(
end_frame,
max(
[end_frame] + [dkp["co"][0] for dkp in dumped_fcurve["keyframe_points"]]
)
)
begin_frame = 0
loader = utils.dump_anything.Loader()
for dumped_fcurve in data["fcurves"]:
dumped_data_path = dumped_fcurve["data_path"]
dumped_array_index = dumped_fcurve["dumped_array_index"]
@ -65,53 +214,14 @@ class BlAction(BlDatablock):
if fcurve is None:
fcurve = target.fcurves.new(dumped_data_path, index=dumped_array_index)
# remove keyframes within dumped_action range
for keyframe in reversed(fcurve.keyframe_points):
if end_frame >= (keyframe.co[0] + begin_frame ) >= begin_frame:
fcurve.keyframe_points.remove(keyframe, fast=True)
# paste dumped keyframes
for dumped_keyframe_point in dumped_fcurve["keyframe_points"]:
if dumped_keyframe_point['type'] == '':
dumped_keyframe_point['type'] = 'KEYFRAME'
new_kf = fcurve.keyframe_points.insert(
dumped_keyframe_point["co"][0] - begin_frame,
dumped_keyframe_point["co"][1],
options={'FAST', 'REPLACE'}
)
keycache = copy.copy(dumped_keyframe_point)
keycache = utils.dump_anything.remove_items_from_dict(
keycache,
["co", "handle_left", "handle_right",'type']
)
loader.load(
new_kf,
keycache
)
new_kf.type = dumped_keyframe_point['type']
new_kf.handle_left = [
dumped_keyframe_point["handle_left"][0] - begin_frame,
dumped_keyframe_point["handle_left"][1]
]
new_kf.handle_right = [
dumped_keyframe_point["handle_right"][0] - begin_frame,
dumped_keyframe_point["handle_right"][1]
]
# clearing (needed for blender to update well)
if len(fcurve.keyframe_points) == 0:
target.fcurves.remove(fcurve)
target.id_root= data['id_root']
load_fcurve(dumped_fcurve, fcurve)
target.id_root = data['id_root']
def _dump(self, pointer=None):
start = utils.current_milli_time()
assert(pointer)
dumper = utils.dump_anything.Dumper()
dumper.exclude_filter =[
dumper.exclude_filter = [
'name_full',
'original',
'use_fake_user',
@ -124,27 +234,14 @@ class BlAction(BlDatablock):
'users'
]
dumper.depth = 1
data = dumper.dump(pointer)
data = dumper.dump(pointer)
data["fcurves"] = []
dumper.depth = 2
for fcurve in self.pointer.fcurves:
fc = {
"data_path": fcurve.data_path,
"dumped_array_index": fcurve.array_index,
"keyframe_points": []
}
for k in fcurve.keyframe_points:
fc["keyframe_points"].append(
dumper.dump(k)
)
data["fcurves"].append(fc)
data["fcurves"].append(dump_fcurve(fcurve, use_numpy=True))
logger.error(
f"{self.pointer.name} dumping time: {utils.current_milli_time()-start} ms")
return data