feat: basic action support

Feat action implementation(load and dump action fcurve).
Append action reference management into bl_datablock.
Updated each concerned implementation to use the new action managment from bl_datablock
Related to #27.
This commit is contained in:
Swann Martinez
2019-11-06 14:30:16 +01:00
parent 99649f588c
commit 7bf676020d
15 changed files with 131 additions and 38 deletions

View File

@ -7,22 +7,71 @@ from .bl_datablock import BlDatablock
# WIP
class BlAction(BlDatablock):
def load(self, data, target):
utils.dump_anything.load(target, data)
def construct(self, data):
return bpy.data.actions.new(data["name"])
def load(self, data, target):
pass
# # find target object
# object_ = bpy.context.scene.objects.active
# if object_ is None:
# raise RuntimeError("Nothing is selected.")
# if object_.mode != 'POSE': # object must be in pose mode
# raise RuntimeError("Object must be in pose mode.")
# if object_.animation_data.action is None:
# raise RuntimeError("Object needs an active action.")
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"]
# create fcurve if needed
fcurve = target.fcurves.find(dumped_data_path, index=dumped_array_index)
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"]:
new_kf = fcurve.keyframe_points.insert(
dumped_keyframe_point["co"][0] - begin_frame,
dumped_keyframe_point["co"][1],
options={'FAST', 'REPLACE'}
)
loader.load(
new_kf,
utils.dump_anything.remove_items_from_dict(
dumped_keyframe_point,
["co", "handle_left", "handle_right"]
)
)
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)
def dump(self, pointer=None):
assert(pointer)
@ -30,7 +79,18 @@ class BlAction(BlDatablock):
dumper = utils.dump_anything.Dumper()
dumper.depth = 2
dumper.exclude_filter =[
'name_full',
'original',
'use_fake_user',
'user',
'is_library_indirect',
'id_root',
'select_control_point',
'select_right_handle',
'select_left_handle',
'uuid'
]
data["fcurves"] = []
for fcurve in self.pointer.fcurves:
@ -53,9 +113,6 @@ class BlAction(BlDatablock):
assert(self.data)
self.pointer = bpy.data.actions.get(self.data['name'])
def diff(self):
return False
def is_valid(self):
return bpy.data.actions.get(self.data['name'])

View File

@ -76,7 +76,7 @@ class BlArmature(BlDatablock):
else:
raise Exception("Wrong context")
def dump(self, pointer=None):
def dump_implementation(self, data, pointer=None):
assert(pointer)
dumper = utils.dump_anything.Dumper()

View File

@ -18,7 +18,7 @@ class BlCamera(BlDatablock):
def construct(self, data):
return bpy.data.cameras.new(data["name"])
def dump(self, pointer=None):
def dump_implementation(self, data, pointer=None):
assert(pointer)
dumper = utils.dump_anything.Dumper()

View File

@ -47,7 +47,7 @@ class BlCollection(BlDatablock):
if collection.uuid not in data["children"]:
target.children.unlink(collection)
def dump(self, pointer=None):
def dump_implementation(self, data, pointer=None):
assert(pointer)
data = {}
data['name'] = pointer.name

View File

@ -29,7 +29,7 @@ class BlCurve(BlDatablock):
utils.dump_anything.load(
new_spline.points[point_index], data['splines'][spline]["points"][point_index])
def dump(self, pointer=None):
def dump_implementation(self, data, pointer=None):
assert(pointer)
data = utils.dump_datablock(pointer, 1)
data['splines'] = {}

View File

@ -5,6 +5,10 @@ from .. import utils
from ..libs.replication.replication.data import ReplicatedDatablock
from ..libs.replication.replication.constants import UP
def has_animation(target):
return (hasattr(target, 'animation_data') \
and target.animation_data \
and target.animation_data.action)
class BlDatablock(ReplicatedDatablock):
def __init__(self, *args, **kwargs):
@ -14,7 +18,7 @@ class BlDatablock(ReplicatedDatablock):
# TODO: use is_library_indirect
self.is_library = (pointer and hasattr(pointer, 'library') and
pointer.library) or \
(self.data and 'library' in self.data)
(self.data and 'library' in self.data)
if self.is_library:
self.load = self.load_library
@ -50,13 +54,42 @@ class BlDatablock(ReplicatedDatablock):
def resolve_dependencies_library(self):
return [self.pointer.library]
def dump(self, pointer=None):
data = {}
if has_animation(pointer):
dumper = utils.dump_anything.Dumper()
dumper.include_filter = ['action']
data['animation_data'] = dumper.dump(pointer.animation_data)
data.update(self.dump_implementation(data, pointer=pointer))
return data
def dump_implementation(self, data, target):
raise NotImplementedError
def load(self, data, target):
# Load animation data
if 'animation_data' in data.keys():
if target.animation_data is None:
target.animation_data_create()
target.animation_data.action = bpy.data.actions[data['animation_data']['action']]
self.load_implementation(data, target)
def load_implementation(self, data, target):
raise NotImplementedError
def resolve_dependencies(self):
dependencies = []
if hasattr(self.pointer,'animation_data') and self.pointer.animation_data:
if has_animation(self.pointer):
dependencies.append(self.pointer.animation_data.action)
return dependencies
def is_valid(self):
raise NotImplementedError

View File

@ -57,7 +57,7 @@ class BlGpencil(BlDatablock):
for mat in data['materials']:
target.materials.append(bpy.data.materials[mat])
def dump(self, pointer=None):
def dump_implementation(self, data, pointer=None):
assert(pointer)
data = utils.dump_datablock(pointer, 2)
utils.dump_datablock_attibutes(

View File

@ -46,7 +46,7 @@ class BlImage(BlDatablock):
image.filepath = img_path
def dump(self, pointer=None):
def dump_implementation(self, data, pointer=None):
assert(pointer)
data = {}
data['pixels'] = dump_image(pointer)

View File

@ -12,7 +12,7 @@ class BlLight(BlDatablock):
def load(self, data, target):
utils.dump_anything.load(target, data)
def dump(self, pointer=None):
def dump_implementation(self, data, pointer=None):
assert(pointer)
dumper = utils.dump_anything.Dumper()
dumper.depth = 3

View File

@ -98,7 +98,7 @@ class BlMaterial(BlDatablock):
for link in data["node_tree"]["links"]:
load_link(target.node_tree, data["node_tree"]["links"][link])
def dump(self, pointer=None):
def dump_implementation(self, data, pointer=None):
assert(pointer)
mat_dumper = utils.dump_anything.Dumper()
mat_dumper.depth = 2

View File

@ -139,7 +139,7 @@ class BlMesh(BlDatablock):
def dump(self, pointer=None):
def dump_implementation(self, data, pointer=None):
assert(pointer)
data = utils.dump_datablock(pointer, 2)

View File

@ -17,7 +17,7 @@ class BlMetaball(BlDatablock):
new_element = target.elements.new(type=data["elements"][element]['type'])
utils.dump_anything.load(new_element, data["elements"][element])
def dump(self, pointer=None):
def dump_implementation(self, data, pointer=None):
assert(pointer)
dumper = utils.dump_anything.Dumper()
dumper.depth = 3

View File

@ -43,8 +43,9 @@ class BlObject(BlDatablock):
return instance
def load(self, data, target):
target.matrix_world = mathutils.Matrix(data["matrix_world"])
def load_implementation(self, data, target):
if "matrix_world" in data:
target.matrix_world = mathutils.Matrix(data["matrix_world"])
target.name = data["name"]
# Load modifiers
@ -76,13 +77,12 @@ class BlObject(BlDatablock):
if data['instance_type'] == 'COLLECTION':
target.instance_collection = bpy.data.collections[data['instance_collection']]
def dump(self, pointer=None):
def dump_implementation(self, data, pointer=None):
assert(pointer)
dumper = utils.dump_anything.Dumper()
dumper.depth = 1
dumper.include_filter = [
"name",
"matrix_world",
"rotation_mode",
"parent",
"data",
@ -93,6 +93,9 @@ class BlObject(BlDatablock):
"instance_collection",
"instance_type"
]
if not pointer.animation_data:
dumper.include_filter.append('matrix_world')
data = dumper.dump(pointer)
if self.is_library:
@ -118,7 +121,7 @@ class BlObject(BlDatablock):
self.pointer = utils.find_from_attr('uuid', self.uuid, bpy.data.objects)
def resolve_dependencies(self):
deps = []
deps = super().resolve_dependencies()
# Avoid Empty case
if self.pointer.data:

View File

@ -42,7 +42,7 @@ class BlScene(BlDatablock):
if 'grease_pencil' in data.keys():
target.grease_pencil = bpy.data.grease_pencils[data['grease_pencil']]
def dump(self, pointer=None):
def dump_implementation(self, data, pointer=None):
assert(pointer)
data = {}

View File

@ -26,7 +26,7 @@ class BlWorld(BlDatablock):
for link in data["node_tree"]["links"]:
load_link(target.node_tree, data["node_tree"]["links"][link])
def dump(self, pointer=None):
def dump_implementation(self, data, pointer=None):
assert(pointer)
world_dumper = utils.dump_anything.Dumper()