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:
@ -7,22 +7,71 @@ from .bl_datablock import BlDatablock
|
|||||||
# WIP
|
# WIP
|
||||||
|
|
||||||
class BlAction(BlDatablock):
|
class BlAction(BlDatablock):
|
||||||
def load(self, data, target):
|
|
||||||
utils.dump_anything.load(target, data)
|
|
||||||
|
|
||||||
def construct(self, data):
|
def construct(self, data):
|
||||||
return bpy.data.actions.new(data["name"])
|
return bpy.data.actions.new(data["name"])
|
||||||
|
|
||||||
def load(self, data, target):
|
def load(self, data, target):
|
||||||
pass
|
begin_frame = 100000
|
||||||
# # find target object
|
end_frame = -100000
|
||||||
# object_ = bpy.context.scene.objects.active
|
|
||||||
# if object_ is None:
|
for dumped_fcurve in data["fcurves"]:
|
||||||
# raise RuntimeError("Nothing is selected.")
|
begin_frame = min(
|
||||||
# if object_.mode != 'POSE': # object must be in pose mode
|
begin_frame,
|
||||||
# raise RuntimeError("Object must be in pose mode.")
|
min(
|
||||||
# if object_.animation_data.action is None:
|
[begin_frame] + [dkp["co"][0] for dkp in dumped_fcurve["keyframe_points"]]
|
||||||
# raise RuntimeError("Object needs an active action.")
|
)
|
||||||
|
)
|
||||||
|
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):
|
def dump(self, pointer=None):
|
||||||
assert(pointer)
|
assert(pointer)
|
||||||
@ -30,7 +79,18 @@ class BlAction(BlDatablock):
|
|||||||
|
|
||||||
dumper = utils.dump_anything.Dumper()
|
dumper = utils.dump_anything.Dumper()
|
||||||
dumper.depth = 2
|
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"] = []
|
data["fcurves"] = []
|
||||||
for fcurve in self.pointer.fcurves:
|
for fcurve in self.pointer.fcurves:
|
||||||
@ -53,9 +113,6 @@ class BlAction(BlDatablock):
|
|||||||
assert(self.data)
|
assert(self.data)
|
||||||
self.pointer = bpy.data.actions.get(self.data['name'])
|
self.pointer = bpy.data.actions.get(self.data['name'])
|
||||||
|
|
||||||
def diff(self):
|
|
||||||
return False
|
|
||||||
|
|
||||||
def is_valid(self):
|
def is_valid(self):
|
||||||
return bpy.data.actions.get(self.data['name'])
|
return bpy.data.actions.get(self.data['name'])
|
||||||
|
|
||||||
|
@ -76,7 +76,7 @@ class BlArmature(BlDatablock):
|
|||||||
else:
|
else:
|
||||||
raise Exception("Wrong context")
|
raise Exception("Wrong context")
|
||||||
|
|
||||||
def dump(self, pointer=None):
|
def dump_implementation(self, data, pointer=None):
|
||||||
assert(pointer)
|
assert(pointer)
|
||||||
|
|
||||||
dumper = utils.dump_anything.Dumper()
|
dumper = utils.dump_anything.Dumper()
|
||||||
|
@ -18,7 +18,7 @@ class BlCamera(BlDatablock):
|
|||||||
def construct(self, data):
|
def construct(self, data):
|
||||||
return bpy.data.cameras.new(data["name"])
|
return bpy.data.cameras.new(data["name"])
|
||||||
|
|
||||||
def dump(self, pointer=None):
|
def dump_implementation(self, data, pointer=None):
|
||||||
assert(pointer)
|
assert(pointer)
|
||||||
|
|
||||||
dumper = utils.dump_anything.Dumper()
|
dumper = utils.dump_anything.Dumper()
|
||||||
|
@ -47,7 +47,7 @@ class BlCollection(BlDatablock):
|
|||||||
if collection.uuid not in data["children"]:
|
if collection.uuid not in data["children"]:
|
||||||
target.children.unlink(collection)
|
target.children.unlink(collection)
|
||||||
|
|
||||||
def dump(self, pointer=None):
|
def dump_implementation(self, data, pointer=None):
|
||||||
assert(pointer)
|
assert(pointer)
|
||||||
data = {}
|
data = {}
|
||||||
data['name'] = pointer.name
|
data['name'] = pointer.name
|
||||||
|
@ -29,7 +29,7 @@ class BlCurve(BlDatablock):
|
|||||||
utils.dump_anything.load(
|
utils.dump_anything.load(
|
||||||
new_spline.points[point_index], data['splines'][spline]["points"][point_index])
|
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)
|
assert(pointer)
|
||||||
data = utils.dump_datablock(pointer, 1)
|
data = utils.dump_datablock(pointer, 1)
|
||||||
data['splines'] = {}
|
data['splines'] = {}
|
||||||
|
@ -5,6 +5,10 @@ from .. import utils
|
|||||||
from ..libs.replication.replication.data import ReplicatedDatablock
|
from ..libs.replication.replication.data import ReplicatedDatablock
|
||||||
from ..libs.replication.replication.constants import UP
|
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):
|
class BlDatablock(ReplicatedDatablock):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
@ -50,13 +54,42 @@ class BlDatablock(ReplicatedDatablock):
|
|||||||
def resolve_dependencies_library(self):
|
def resolve_dependencies_library(self):
|
||||||
return [self.pointer.library]
|
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):
|
def resolve_dependencies(self):
|
||||||
dependencies = []
|
dependencies = []
|
||||||
|
|
||||||
if hasattr(self.pointer,'animation_data') and self.pointer.animation_data:
|
if has_animation(self.pointer):
|
||||||
dependencies.append(self.pointer.animation_data.action)
|
dependencies.append(self.pointer.animation_data.action)
|
||||||
|
|
||||||
return dependencies
|
return dependencies
|
||||||
|
|
||||||
|
|
||||||
def is_valid(self):
|
def is_valid(self):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
@ -57,7 +57,7 @@ class BlGpencil(BlDatablock):
|
|||||||
for mat in data['materials']:
|
for mat in data['materials']:
|
||||||
target.materials.append(bpy.data.materials[mat])
|
target.materials.append(bpy.data.materials[mat])
|
||||||
|
|
||||||
def dump(self, pointer=None):
|
def dump_implementation(self, data, pointer=None):
|
||||||
assert(pointer)
|
assert(pointer)
|
||||||
data = utils.dump_datablock(pointer, 2)
|
data = utils.dump_datablock(pointer, 2)
|
||||||
utils.dump_datablock_attibutes(
|
utils.dump_datablock_attibutes(
|
||||||
|
@ -46,7 +46,7 @@ class BlImage(BlDatablock):
|
|||||||
image.filepath = img_path
|
image.filepath = img_path
|
||||||
|
|
||||||
|
|
||||||
def dump(self, pointer=None):
|
def dump_implementation(self, data, pointer=None):
|
||||||
assert(pointer)
|
assert(pointer)
|
||||||
data = {}
|
data = {}
|
||||||
data['pixels'] = dump_image(pointer)
|
data['pixels'] = dump_image(pointer)
|
||||||
|
@ -12,7 +12,7 @@ class BlLight(BlDatablock):
|
|||||||
def load(self, data, target):
|
def load(self, data, target):
|
||||||
utils.dump_anything.load(target, data)
|
utils.dump_anything.load(target, data)
|
||||||
|
|
||||||
def dump(self, pointer=None):
|
def dump_implementation(self, data, pointer=None):
|
||||||
assert(pointer)
|
assert(pointer)
|
||||||
dumper = utils.dump_anything.Dumper()
|
dumper = utils.dump_anything.Dumper()
|
||||||
dumper.depth = 3
|
dumper.depth = 3
|
||||||
|
@ -98,7 +98,7 @@ class BlMaterial(BlDatablock):
|
|||||||
for link in data["node_tree"]["links"]:
|
for link in data["node_tree"]["links"]:
|
||||||
load_link(target.node_tree, data["node_tree"]["links"][link])
|
load_link(target.node_tree, data["node_tree"]["links"][link])
|
||||||
|
|
||||||
def dump(self, pointer=None):
|
def dump_implementation(self, data, pointer=None):
|
||||||
assert(pointer)
|
assert(pointer)
|
||||||
mat_dumper = utils.dump_anything.Dumper()
|
mat_dumper = utils.dump_anything.Dumper()
|
||||||
mat_dumper.depth = 2
|
mat_dumper.depth = 2
|
||||||
|
@ -139,7 +139,7 @@ class BlMesh(BlDatablock):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
def dump(self, pointer=None):
|
def dump_implementation(self, data, pointer=None):
|
||||||
assert(pointer)
|
assert(pointer)
|
||||||
|
|
||||||
data = utils.dump_datablock(pointer, 2)
|
data = utils.dump_datablock(pointer, 2)
|
||||||
|
@ -17,7 +17,7 @@ class BlMetaball(BlDatablock):
|
|||||||
new_element = target.elements.new(type=data["elements"][element]['type'])
|
new_element = target.elements.new(type=data["elements"][element]['type'])
|
||||||
utils.dump_anything.load(new_element, data["elements"][element])
|
utils.dump_anything.load(new_element, data["elements"][element])
|
||||||
|
|
||||||
def dump(self, pointer=None):
|
def dump_implementation(self, data, pointer=None):
|
||||||
assert(pointer)
|
assert(pointer)
|
||||||
dumper = utils.dump_anything.Dumper()
|
dumper = utils.dump_anything.Dumper()
|
||||||
dumper.depth = 3
|
dumper.depth = 3
|
||||||
|
@ -43,7 +43,8 @@ class BlObject(BlDatablock):
|
|||||||
|
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
def load(self, data, target):
|
def load_implementation(self, data, target):
|
||||||
|
if "matrix_world" in data:
|
||||||
target.matrix_world = mathutils.Matrix(data["matrix_world"])
|
target.matrix_world = mathutils.Matrix(data["matrix_world"])
|
||||||
|
|
||||||
target.name = data["name"]
|
target.name = data["name"]
|
||||||
@ -76,13 +77,12 @@ class BlObject(BlDatablock):
|
|||||||
if data['instance_type'] == 'COLLECTION':
|
if data['instance_type'] == 'COLLECTION':
|
||||||
target.instance_collection = bpy.data.collections[data['instance_collection']]
|
target.instance_collection = bpy.data.collections[data['instance_collection']]
|
||||||
|
|
||||||
def dump(self, pointer=None):
|
def dump_implementation(self, data, pointer=None):
|
||||||
assert(pointer)
|
assert(pointer)
|
||||||
dumper = utils.dump_anything.Dumper()
|
dumper = utils.dump_anything.Dumper()
|
||||||
dumper.depth = 1
|
dumper.depth = 1
|
||||||
dumper.include_filter = [
|
dumper.include_filter = [
|
||||||
"name",
|
"name",
|
||||||
"matrix_world",
|
|
||||||
"rotation_mode",
|
"rotation_mode",
|
||||||
"parent",
|
"parent",
|
||||||
"data",
|
"data",
|
||||||
@ -93,6 +93,9 @@ class BlObject(BlDatablock):
|
|||||||
"instance_collection",
|
"instance_collection",
|
||||||
"instance_type"
|
"instance_type"
|
||||||
]
|
]
|
||||||
|
if not pointer.animation_data:
|
||||||
|
dumper.include_filter.append('matrix_world')
|
||||||
|
|
||||||
data = dumper.dump(pointer)
|
data = dumper.dump(pointer)
|
||||||
|
|
||||||
if self.is_library:
|
if self.is_library:
|
||||||
@ -118,7 +121,7 @@ class BlObject(BlDatablock):
|
|||||||
self.pointer = utils.find_from_attr('uuid', self.uuid, bpy.data.objects)
|
self.pointer = utils.find_from_attr('uuid', self.uuid, bpy.data.objects)
|
||||||
|
|
||||||
def resolve_dependencies(self):
|
def resolve_dependencies(self):
|
||||||
deps = []
|
deps = super().resolve_dependencies()
|
||||||
|
|
||||||
# Avoid Empty case
|
# Avoid Empty case
|
||||||
if self.pointer.data:
|
if self.pointer.data:
|
||||||
|
@ -42,7 +42,7 @@ class BlScene(BlDatablock):
|
|||||||
if 'grease_pencil' in data.keys():
|
if 'grease_pencil' in data.keys():
|
||||||
target.grease_pencil = bpy.data.grease_pencils[data['grease_pencil']]
|
target.grease_pencil = bpy.data.grease_pencils[data['grease_pencil']]
|
||||||
|
|
||||||
def dump(self, pointer=None):
|
def dump_implementation(self, data, pointer=None):
|
||||||
assert(pointer)
|
assert(pointer)
|
||||||
data = {}
|
data = {}
|
||||||
|
|
||||||
|
@ -26,7 +26,7 @@ class BlWorld(BlDatablock):
|
|||||||
for link in data["node_tree"]["links"]:
|
for link in data["node_tree"]["links"]:
|
||||||
load_link(target.node_tree, data["node_tree"]["links"][link])
|
load_link(target.node_tree, data["node_tree"]["links"][link])
|
||||||
|
|
||||||
def dump(self, pointer=None):
|
def dump_implementation(self, data, pointer=None):
|
||||||
assert(pointer)
|
assert(pointer)
|
||||||
|
|
||||||
world_dumper = utils.dump_anything.Dumper()
|
world_dumper = utils.dump_anything.Dumper()
|
||||||
|
Reference in New Issue
Block a user