298 lines
8.8 KiB
Python
298 lines
8.8 KiB
Python
# ##### BEGIN GPL LICENSE BLOCK #####
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
#
|
|
# ##### END GPL LICENSE BLOCK #####
|
|
|
|
|
|
import bpy
|
|
import mathutils
|
|
import copy
|
|
import numpy as np
|
|
from enum import Enum
|
|
|
|
from .. import utils
|
|
from .dump_anything import (
|
|
Dumper,
|
|
Loader,
|
|
np_dump_collection,
|
|
np_load_collection,
|
|
remove_items_from_dict)
|
|
from .bl_datablock import stamp_uuid
|
|
from replication.protocol import ReplicatedDatablock
|
|
from replication.objects import Node
|
|
|
|
KEYFRAME = [
|
|
'amplitude',
|
|
'co',
|
|
'back',
|
|
'handle_left',
|
|
'handle_right',
|
|
'easing',
|
|
'handle_left_type',
|
|
'handle_right_type',
|
|
'type',
|
|
'interpolation',
|
|
]
|
|
|
|
|
|
def has_action(datablock):
|
|
""" Check if the datablock datablock has actions
|
|
"""
|
|
return (hasattr(datablock, 'animation_data')
|
|
and datablock.animation_data
|
|
and datablock.animation_data.action)
|
|
|
|
|
|
def has_driver(datablock):
|
|
""" Check if the datablock datablock is driven
|
|
"""
|
|
return (hasattr(datablock, 'animation_data')
|
|
and datablock.animation_data
|
|
and datablock.animation_data.drivers)
|
|
|
|
|
|
def dump_driver(driver):
|
|
dumper = Dumper()
|
|
dumper.depth = 6
|
|
data = dumper.dump(driver)
|
|
|
|
return data
|
|
|
|
|
|
def load_driver(target_datablock, src_driver):
|
|
loader = Loader()
|
|
drivers = target_datablock.animation_data.drivers
|
|
src_driver_data = src_driver['driver']
|
|
new_driver = drivers.new(
|
|
src_driver['data_path'], index=src_driver['array_index'])
|
|
|
|
# Settings
|
|
new_driver.driver.type = src_driver_data['type']
|
|
new_driver.driver.expression = src_driver_data['expression']
|
|
loader.load(new_driver, src_driver)
|
|
|
|
# Variables
|
|
for src_variable in src_driver_data['variables']:
|
|
src_var_data = src_driver_data['variables'][src_variable]
|
|
new_var = new_driver.driver.variables.new()
|
|
new_var.name = src_var_data['name']
|
|
new_var.type = src_var_data['type']
|
|
|
|
for src_target in src_var_data['targets']:
|
|
src_target_data = src_var_data['targets'][src_target]
|
|
new_var.targets[src_target].id = utils.resolve_from_id(
|
|
src_target_data['id'], src_target_data['id_type'])
|
|
loader.load(
|
|
new_var.targets[src_target], src_target_data)
|
|
|
|
# Fcurve
|
|
new_fcurve = new_driver.keyframe_points
|
|
for p in reversed(new_fcurve):
|
|
new_fcurve.remove(p, fast=True)
|
|
|
|
new_fcurve.add(len(src_driver['keyframe_points']))
|
|
|
|
for index, src_point in enumerate(src_driver['keyframe_points']):
|
|
new_point = new_fcurve[index]
|
|
loader.load(new_point, src_driver['keyframe_points'][src_point])
|
|
|
|
|
|
def dump_fcurve(fcurve: bpy.types.FCurve, use_numpy: bool = True) -> dict:
|
|
""" 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:
|
|
points = fcurve.keyframe_points
|
|
fcurve_data['keyframes_count'] = len(fcurve.keyframe_points)
|
|
fcurve_data['keyframe_points'] = np_dump_collection(points, KEYFRAME)
|
|
|
|
else: # Legacy method
|
|
dumper = 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:
|
|
keyframe_points.add(fcurve_data['keyframes_count'])
|
|
np_load_collection(
|
|
fcurve_data["keyframe_points"], keyframe_points, KEYFRAME)
|
|
|
|
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 = remove_items_from_dict(
|
|
keycache,
|
|
["co", "handle_left", "handle_right", 'type']
|
|
)
|
|
|
|
loader = Loader()
|
|
loader.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()
|
|
|
|
|
|
def dump_animation_data(datablock, data):
|
|
if has_action(datablock):
|
|
dumper = Dumper()
|
|
dumper.include_filter = ['action']
|
|
data['animation_data'] = dumper.dump(datablock.animation_data)
|
|
|
|
if has_driver(datablock):
|
|
dumped_drivers = {'animation_data': {'drivers': []}}
|
|
for driver in datablock.animation_data.drivers:
|
|
dumped_drivers['animation_data']['drivers'].append(
|
|
dump_driver(driver))
|
|
|
|
data.update(dumped_drivers)
|
|
|
|
|
|
def load_animation_data(data, datablock):
|
|
# Load animation data
|
|
if 'animation_data' in data.keys():
|
|
if datablock.animation_data is None:
|
|
datablock.animation_data_create()
|
|
|
|
for d in datablock.animation_data.drivers:
|
|
datablock.animation_data.drivers.remove(d)
|
|
|
|
if 'drivers' in data['animation_data']:
|
|
for driver in data['animation_data']['drivers']:
|
|
load_driver(datablock, driver)
|
|
|
|
if 'action' in data['animation_data']:
|
|
datablock.animation_data.action = bpy.data.actions[data['animation_data']['action']]
|
|
# Remove existing animation data if there is not more to load
|
|
elif hasattr(datablock, 'animation_data') and datablock.animation_data:
|
|
datablock.animation_data_clear()
|
|
|
|
|
|
def resolve_animation_dependencies(datablock):
|
|
if has_action(datablock):
|
|
return [datablock.animation_data.action]
|
|
else:
|
|
return []
|
|
|
|
|
|
class BlAction(ReplicatedDatablock):
|
|
bl_id = "actions"
|
|
bl_class = bpy.types.Action
|
|
bl_check_common = False
|
|
bl_icon = 'ACTION_TWEAK'
|
|
bl_reload_parent = False
|
|
|
|
@staticmethod
|
|
def construct(data: dict) -> object:
|
|
return bpy.data.actions.new(data["name"])
|
|
|
|
@staticmethod
|
|
def load(data: dict, datablock: object):
|
|
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 = datablock.fcurves.find(
|
|
dumped_data_path, index=dumped_array_index)
|
|
if fcurve is None:
|
|
fcurve = datablock.fcurves.new(
|
|
dumped_data_path, index=dumped_array_index)
|
|
|
|
load_fcurve(dumped_fcurve, fcurve)
|
|
|
|
id_root = data.get('id_root')
|
|
|
|
if id_root:
|
|
datablock.id_root = id_root
|
|
|
|
@staticmethod
|
|
def dump(datablock: object) -> dict:
|
|
stamp_uuid(datablock)
|
|
|
|
dumper = Dumper()
|
|
dumper.exclude_filter = [
|
|
'name_full',
|
|
'original',
|
|
'use_fake_user',
|
|
'user',
|
|
'is_library_indirect',
|
|
'select_control_point',
|
|
'select_right_handle',
|
|
'select_left_handle',
|
|
'uuid',
|
|
'users'
|
|
]
|
|
dumper.depth = 1
|
|
data = dumper.dump(datablock)
|
|
|
|
data["fcurves"] = []
|
|
|
|
for fcurve in datablock.fcurves:
|
|
data["fcurves"].append(dump_fcurve(fcurve, use_numpy=True))
|
|
|
|
return data
|