refactor: move implementation to static def
This commit is contained in:
@ -41,7 +41,7 @@ import bpy
|
||||
from bpy.app.handlers import persistent
|
||||
|
||||
from . import environment
|
||||
|
||||
from uuid import uuid4
|
||||
LIBS = os.path.dirname(os.path.abspath(__file__))+"/libs/replication"
|
||||
|
||||
module_error_msg = "Insufficient rights to install the multi-user \
|
||||
@ -53,10 +53,11 @@ def register():
|
||||
datefmt='%H:%M:%S',
|
||||
level=logging.INFO)
|
||||
|
||||
for module_name in list(sys.modules.keys()):
|
||||
if 'replication' in module_name:
|
||||
del sys.modules[module_name]
|
||||
|
||||
if LIBS in sys.path:
|
||||
logging.debug('Third party module already added')
|
||||
else:
|
||||
if LIBS not in sys.path:
|
||||
logging.info('Adding local modules dir to the path')
|
||||
sys.path.insert(0, LIBS)
|
||||
|
||||
|
@ -20,34 +20,34 @@ import bpy
|
||||
__all__ = [
|
||||
'bl_object',
|
||||
'bl_mesh',
|
||||
'bl_camera',
|
||||
# 'bl_camera',
|
||||
'bl_collection',
|
||||
'bl_curve',
|
||||
'bl_gpencil',
|
||||
'bl_image',
|
||||
'bl_light',
|
||||
# 'bl_curve',
|
||||
# 'bl_gpencil',
|
||||
# 'bl_image',
|
||||
# 'bl_light',
|
||||
'bl_scene',
|
||||
'bl_material',
|
||||
'bl_library',
|
||||
'bl_armature',
|
||||
'bl_action',
|
||||
'bl_world',
|
||||
'bl_metaball',
|
||||
'bl_lattice',
|
||||
'bl_lightprobe',
|
||||
'bl_speaker',
|
||||
'bl_font',
|
||||
'bl_sound',
|
||||
'bl_file',
|
||||
# 'bl_sequencer',
|
||||
'bl_node_group',
|
||||
'bl_texture',
|
||||
# 'bl_library',
|
||||
# 'bl_armature',
|
||||
# 'bl_action',
|
||||
# 'bl_world',
|
||||
# 'bl_metaball',
|
||||
# 'bl_lattice',
|
||||
# 'bl_lightprobe',
|
||||
# 'bl_speaker',
|
||||
# 'bl_font',
|
||||
# 'bl_sound',
|
||||
# 'bl_file',
|
||||
# # 'bl_sequencer',
|
||||
# 'bl_node_group',
|
||||
# 'bl_texture',
|
||||
] # Order here defines execution order
|
||||
|
||||
if bpy.app.version[1] >= 91:
|
||||
__all__.append('bl_volume')
|
||||
# if bpy.app.version[1] >= 91:
|
||||
# __all__.append('bl_volume')
|
||||
|
||||
from . import *
|
||||
from . import bl_object, bl_action, bl_scene, bl_mesh, bl_collection
|
||||
from replication.protocol import DataTranslationProtocol
|
||||
|
||||
def types_to_register():
|
||||
|
@ -24,9 +24,14 @@ 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 BlDatablock
|
||||
|
||||
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',
|
||||
@ -42,6 +47,68 @@ KEYFRAME = [
|
||||
]
|
||||
|
||||
|
||||
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
|
||||
|
||||
@ -129,26 +196,68 @@ def load_fcurve(fcurve_data, fcurve):
|
||||
fcurve.update()
|
||||
|
||||
|
||||
class BlAction(BlDatablock):
|
||||
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 get_animation_dependencies(datablock):
|
||||
if has_action(datablock):
|
||||
return datablock.animation_data.action
|
||||
|
||||
|
||||
class BlAction(ReplicatedDatablock):
|
||||
bl_id = "actions"
|
||||
bl_class = bpy.types.Action
|
||||
bl_check_common = False
|
||||
bl_icon = 'ACTION_TWEAK'
|
||||
bl_reload_parent = False
|
||||
|
||||
def construct(self, data):
|
||||
@staticmethod
|
||||
def construct(data: dict) -> object:
|
||||
return bpy.data.actions.new(data["name"])
|
||||
|
||||
def _load_implementation(self, data, target):
|
||||
@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 = target.fcurves.find(
|
||||
fcurve = datablock.fcurves.find(
|
||||
dumped_data_path, index=dumped_array_index)
|
||||
if fcurve is None:
|
||||
fcurve = target.fcurves.new(
|
||||
fcurve = datablock.fcurves.new(
|
||||
dumped_data_path, index=dumped_array_index)
|
||||
|
||||
load_fcurve(dumped_fcurve, fcurve)
|
||||
@ -156,9 +265,12 @@ class BlAction(BlDatablock):
|
||||
id_root = data.get('id_root')
|
||||
|
||||
if id_root:
|
||||
target.id_root = id_root
|
||||
datablock.id_root = id_root
|
||||
|
||||
@staticmethod
|
||||
def dump(datablock: object) -> dict:
|
||||
stamp_uuid(datablock)
|
||||
|
||||
def _dump_implementation(self, data, instance=None):
|
||||
dumper = Dumper()
|
||||
dumper.exclude_filter = [
|
||||
'name_full',
|
||||
@ -173,11 +285,11 @@ class BlAction(BlDatablock):
|
||||
'users'
|
||||
]
|
||||
dumper.depth = 1
|
||||
data = dumper.dump(instance)
|
||||
data = dumper.dump(datablock)
|
||||
|
||||
data["fcurves"] = []
|
||||
|
||||
for fcurve in instance.fcurves:
|
||||
for fcurve in datablock.fcurves:
|
||||
data["fcurves"].append(dump_fcurve(fcurve, use_numpy=True))
|
||||
|
||||
return data
|
||||
|
@ -42,10 +42,10 @@ class BlArmature(BlDatablock):
|
||||
bl_icon = 'ARMATURE_DATA'
|
||||
bl_reload_parent = False
|
||||
|
||||
def construct(self, data):
|
||||
def construct(data: dict) -> object:
|
||||
return bpy.data.armatures.new(data["name"])
|
||||
|
||||
def _load_implementation(self, data, target):
|
||||
def load(data: dict, datablock: object):
|
||||
# Load parent object
|
||||
parent_object = utils.find_from_attr(
|
||||
'uuid',
|
||||
@ -119,7 +119,7 @@ class BlArmature(BlDatablock):
|
||||
if 'EDIT' in current_mode:
|
||||
bpy.ops.object.mode_set(mode='EDIT')
|
||||
|
||||
def _dump_implementation(self, data, instance=None):
|
||||
def dump(datablock: object) -> dict:
|
||||
assert(instance)
|
||||
|
||||
dumper = Dumper()
|
||||
|
@ -30,11 +30,11 @@ class BlCamera(BlDatablock):
|
||||
bl_icon = 'CAMERA_DATA'
|
||||
bl_reload_parent = False
|
||||
|
||||
def construct(self, data):
|
||||
def construct(data: dict) -> object:
|
||||
return bpy.data.cameras.new(data["name"])
|
||||
|
||||
|
||||
def _load_implementation(self, data, target):
|
||||
def load(data: dict, datablock: object):
|
||||
loader = Loader()
|
||||
loader.load(target, data)
|
||||
|
||||
@ -56,7 +56,7 @@ class BlCamera(BlDatablock):
|
||||
target_img.image = bpy.data.images[img_id]
|
||||
loader.load(target_img, img_data)
|
||||
|
||||
def _dump_implementation(self, data, instance=None):
|
||||
def dump(datablock: object) -> dict:
|
||||
assert(instance)
|
||||
|
||||
# TODO: background image support
|
||||
@ -106,7 +106,7 @@ class BlCamera(BlDatablock):
|
||||
return dumper.dump(instance)
|
||||
|
||||
@staticmethod
|
||||
def _resolve_deps_implementation(datablock):
|
||||
def resolve_deps(datablock: object) -> [object]:
|
||||
deps = []
|
||||
for background in datablock.background_images:
|
||||
if background.image:
|
||||
|
@ -20,9 +20,9 @@ import bpy
|
||||
import mathutils
|
||||
|
||||
from .. import utils
|
||||
from .bl_datablock import BlDatablock
|
||||
from .dump_anything import Loader, Dumper
|
||||
|
||||
from replication.protocol import ReplicatedDatablock
|
||||
from replication.objects import Node
|
||||
|
||||
def dump_collection_children(collection):
|
||||
collection_children = []
|
||||
@ -81,42 +81,37 @@ def resolve_collection_dependencies(collection):
|
||||
|
||||
return deps
|
||||
|
||||
class BlCollection(BlDatablock):
|
||||
class BlCollection(ReplicatedDatablock):
|
||||
bl_id = "collections"
|
||||
bl_icon = 'FILE_FOLDER'
|
||||
bl_class = bpy.types.Collection
|
||||
bl_check_common = True
|
||||
bl_reload_parent = False
|
||||
|
||||
def construct(self, data):
|
||||
if self.is_library:
|
||||
with bpy.data.libraries.load(filepath=bpy.data.libraries[self.data['library']].filepath, link=True) as (sourceData, targetData):
|
||||
targetData.collections = [
|
||||
name for name in sourceData.collections if name == self.data['name']]
|
||||
@staticmethod
|
||||
def construct(data: dict) -> object:
|
||||
datablock = bpy.data.collections.new(node.data["name"])
|
||||
return datablock
|
||||
|
||||
instance = bpy.data.collections[self.data['name']]
|
||||
|
||||
return instance
|
||||
|
||||
instance = bpy.data.collections.new(data["name"])
|
||||
return instance
|
||||
|
||||
def _load_implementation(self, data, target):
|
||||
@staticmethod
|
||||
def load(data: dict, datablock: object):
|
||||
data = node.data
|
||||
loader = Loader()
|
||||
loader.load(target, data)
|
||||
loader.load(datablock, data)
|
||||
|
||||
# Objects
|
||||
load_collection_objects(data['objects'], target)
|
||||
load_collection_objects(data['objects'], datablock)
|
||||
|
||||
# Link childrens
|
||||
load_collection_childrens(data['children'], target)
|
||||
load_collection_childrens(data['children'], datablock)
|
||||
|
||||
# FIXME: Find a better way after the replication big refacotoring
|
||||
# Keep other user from deleting collection object by flushing their history
|
||||
utils.flush_history()
|
||||
|
||||
def _dump_implementation(self, data, instance=None):
|
||||
assert(instance)
|
||||
@staticmethod
|
||||
def dump(datablock: object) -> dict:
|
||||
assert(datablock)
|
||||
|
||||
dumper = Dumper()
|
||||
dumper.depth = 1
|
||||
@ -124,16 +119,16 @@ class BlCollection(BlDatablock):
|
||||
"name",
|
||||
"instance_offset"
|
||||
]
|
||||
data = dumper.dump(instance)
|
||||
data = dumper.dump(datablock)
|
||||
|
||||
# dump objects
|
||||
data['objects'] = dump_collection_objects(instance)
|
||||
data['objects'] = dump_collection_objects(datablock)
|
||||
|
||||
# dump children collections
|
||||
data['children'] = dump_collection_children(instance)
|
||||
data['children'] = dump_collection_children(datablock)
|
||||
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def _resolve_deps_implementation(datablock):
|
||||
def resolve_deps(datablock: object) -> [object]:
|
||||
return resolve_collection_dependencies(datablock)
|
||||
|
@ -141,10 +141,10 @@ class BlCurve(BlDatablock):
|
||||
bl_icon = 'CURVE_DATA'
|
||||
bl_reload_parent = False
|
||||
|
||||
def construct(self, data):
|
||||
def construct(data: dict) -> object:
|
||||
return bpy.data.curves.new(data["name"], data["type"])
|
||||
|
||||
def _load_implementation(self, data, target):
|
||||
def load(data: dict, datablock: object):
|
||||
loader = Loader()
|
||||
loader.load(target, data)
|
||||
|
||||
@ -175,7 +175,7 @@ class BlCurve(BlDatablock):
|
||||
if src_materials:
|
||||
load_materials_slots(src_materials, target.materials)
|
||||
|
||||
def _dump_implementation(self, data, instance=None):
|
||||
def dump(datablock: object) -> dict:
|
||||
assert(instance)
|
||||
dumper = Dumper()
|
||||
# Conflicting attributes
|
||||
@ -223,7 +223,7 @@ class BlCurve(BlDatablock):
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def _resolve_deps_implementation(datablock):
|
||||
def resolve_deps(datablock: object) -> [object]:
|
||||
deps = []
|
||||
curve = datablock
|
||||
|
||||
|
@ -23,72 +23,14 @@ import bpy
|
||||
import mathutils
|
||||
from replication.constants import DIFF_BINARY, DIFF_JSON, UP
|
||||
from replication.protocol import ReplicatedDatablock
|
||||
from replication.objects import Node
|
||||
|
||||
from uuid import uuid4
|
||||
|
||||
from .. import utils
|
||||
from .dump_anything import Dumper, Loader
|
||||
|
||||
|
||||
def has_action(target):
|
||||
""" Check if the target datablock has actions
|
||||
"""
|
||||
return (hasattr(target, 'animation_data')
|
||||
and target.animation_data
|
||||
and target.animation_data.action)
|
||||
|
||||
|
||||
def has_driver(target):
|
||||
""" Check if the target datablock is driven
|
||||
"""
|
||||
return (hasattr(target, 'animation_data')
|
||||
and target.animation_data
|
||||
and target.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 get_datablock_from_uuid(uuid, default, ignore=[]):
|
||||
if not uuid:
|
||||
return default
|
||||
@ -101,118 +43,17 @@ def get_datablock_from_uuid(uuid, default, ignore=[]):
|
||||
return default
|
||||
|
||||
|
||||
class BlDatablock(ReplicatedDatablock):
|
||||
"""BlDatablock
|
||||
|
||||
bl_id : blender internal storage identifier
|
||||
bl_class : blender internal type
|
||||
bl_icon : type icon (blender icon name)
|
||||
bl_check_common: enable check even in common rights
|
||||
bl_reload_parent: reload parent
|
||||
"""
|
||||
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
instance = kwargs.get('instance', None)
|
||||
|
||||
self.preferences = utils.get_preferences()
|
||||
|
||||
# TODO: use is_library_indirect
|
||||
self.is_library = (instance and hasattr(instance, 'library') and
|
||||
instance.library) or \
|
||||
(hasattr(self,'data') and self.data and 'library' in self.data)
|
||||
|
||||
if instance and hasattr(instance, 'uuid'):
|
||||
instance.uuid = self.uuid
|
||||
|
||||
def resolve(self, construct = True):
|
||||
datablock_root = getattr(bpy.data, self.bl_id)
|
||||
datablock_ref = utils.find_from_attr('uuid', self.uuid, datablock_root)
|
||||
def resolve_datablock_from_root(node:Node, root)->object:
|
||||
datablock_ref = utils.find_from_attr('uuid', node.uuid, root)
|
||||
|
||||
if not datablock_ref:
|
||||
try:
|
||||
datablock_ref = datablock_root[self.data['name']]
|
||||
datablock_ref = root[node.data['name']]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if construct and not datablock_ref:
|
||||
name = self.data.get('name')
|
||||
logging.debug(f"Constructing {name}")
|
||||
datablock_ref = self._construct(data=self.data)
|
||||
return datablock_ref
|
||||
|
||||
if datablock_ref is not None:
|
||||
setattr(datablock_ref, 'uuid', self.uuid)
|
||||
self.instance = datablock_ref
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def dump(self, instance=None):
|
||||
dumper = Dumper()
|
||||
data = {}
|
||||
# Dump animation data
|
||||
if has_action(instance):
|
||||
dumper = Dumper()
|
||||
dumper.include_filter = ['action']
|
||||
data['animation_data'] = dumper.dump(instance.animation_data)
|
||||
|
||||
if has_driver(instance):
|
||||
dumped_drivers = {'animation_data': {'drivers': []}}
|
||||
for driver in instance.animation_data.drivers:
|
||||
dumped_drivers['animation_data']['drivers'].append(
|
||||
dump_driver(driver))
|
||||
|
||||
data.update(dumped_drivers)
|
||||
|
||||
if self.is_library:
|
||||
data.update(dumper.dump(instance))
|
||||
else:
|
||||
data.update(self._dump_implementation(data, instance=instance))
|
||||
|
||||
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()
|
||||
|
||||
for d in target.animation_data.drivers:
|
||||
target.animation_data.drivers.remove(d)
|
||||
|
||||
if 'drivers' in data['animation_data']:
|
||||
for driver in data['animation_data']['drivers']:
|
||||
load_driver(target, driver)
|
||||
|
||||
if 'action' in data['animation_data']:
|
||||
target.animation_data.action = bpy.data.actions[data['animation_data']['action']]
|
||||
# Remove existing animation data if there is not more to load
|
||||
elif hasattr(target, 'animation_data') and target.animation_data:
|
||||
target.animation_data_clear()
|
||||
|
||||
if self.is_library:
|
||||
return
|
||||
else:
|
||||
self._load_implementation(data, target)
|
||||
|
||||
def _load_implementation(self, data, target):
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
def resolve_deps(self, datablock):
|
||||
dependencies = []
|
||||
|
||||
if has_action(datablock):
|
||||
dependencies.append(datablock.animation_data.action)
|
||||
|
||||
dependencies.extend(self._resolve_deps_implementation(datablock))
|
||||
|
||||
return dependencies
|
||||
|
||||
@staticmethod
|
||||
def _resolve_deps_implementation(datablock):
|
||||
return []
|
||||
def stamp_uuid(datablock):
|
||||
if not datablock.uuid:
|
||||
datablock.uuid = str(uuid4())
|
@ -34,7 +34,7 @@ class BlFont(BlDatablock):
|
||||
bl_icon = 'FILE_FONT'
|
||||
bl_reload_parent = False
|
||||
|
||||
def construct(self, data):
|
||||
def construct(data: dict) -> object:
|
||||
filename = data.get('filename')
|
||||
|
||||
if filename == '<builtin>':
|
||||
@ -63,7 +63,7 @@ class BlFont(BlDatablock):
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def _resolve_deps_implementation(datablock):
|
||||
def resolve_deps(datablock: object) -> [object]:
|
||||
deps = []
|
||||
if datablock.filepath and datablock.filepath != '<builtin>':
|
||||
ensure_unpacked(datablock)
|
||||
|
@ -235,10 +235,10 @@ class BlGpencil(BlDatablock):
|
||||
bl_icon = 'GREASEPENCIL'
|
||||
bl_reload_parent = False
|
||||
|
||||
def construct(self, data):
|
||||
def construct(data: dict) -> object:
|
||||
return bpy.data.grease_pencils.new(data["name"])
|
||||
|
||||
def _load_implementation(self, data, target):
|
||||
def load(data: dict, datablock: object):
|
||||
target.materials.clear()
|
||||
if "materials" in data.keys():
|
||||
for mat in data['materials']:
|
||||
@ -267,7 +267,7 @@ class BlGpencil(BlDatablock):
|
||||
|
||||
|
||||
|
||||
def _dump_implementation(self, data, instance=None):
|
||||
def dump(datablock: object) -> dict:
|
||||
assert(instance)
|
||||
dumper = Dumper()
|
||||
dumper.depth = 2
|
||||
@ -291,7 +291,7 @@ class BlGpencil(BlDatablock):
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def _resolve_deps_implementation(datablock):
|
||||
def resolve_deps(datablock: object) -> [object]:
|
||||
deps = []
|
||||
|
||||
for material in datablock.materials:
|
||||
|
@ -55,7 +55,7 @@ class BlImage(BlDatablock):
|
||||
bl_icon = 'IMAGE_DATA'
|
||||
bl_reload_parent = False
|
||||
|
||||
def construct(self, data):
|
||||
def construct(data: dict) -> object:
|
||||
return bpy.data.images.new(
|
||||
name=data['name'],
|
||||
width=data['size'][0],
|
||||
@ -102,7 +102,7 @@ class BlImage(BlDatablock):
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def _resolve_deps_implementation(datablock):
|
||||
def resolve_deps(datablock: object) -> [object]:
|
||||
deps = []
|
||||
|
||||
if datablock.packed_file:
|
||||
|
@ -33,10 +33,10 @@ class BlLattice(BlDatablock):
|
||||
bl_icon = 'LATTICE_DATA'
|
||||
bl_reload_parent = False
|
||||
|
||||
def construct(self, data):
|
||||
def construct(data: dict) -> object:
|
||||
return bpy.data.lattices.new(data["name"])
|
||||
|
||||
def _load_implementation(self, data, target):
|
||||
def load(data: dict, datablock: object):
|
||||
if target.is_editmode:
|
||||
raise ContextError("lattice is in edit mode")
|
||||
|
||||
@ -45,7 +45,7 @@ class BlLattice(BlDatablock):
|
||||
|
||||
np_load_collection(data['points'], target.points, POINT)
|
||||
|
||||
def _dump_implementation(self, data, instance=None):
|
||||
def dump(datablock: object) -> dict:
|
||||
if instance.is_editmode:
|
||||
raise ContextError("lattice is in edit mode")
|
||||
|
||||
|
@ -30,7 +30,7 @@ class BlLibrary(BlDatablock):
|
||||
bl_icon = 'LIBRARY_DATA_DIRECT'
|
||||
bl_reload_parent = False
|
||||
|
||||
def construct(self, data):
|
||||
def construct(data: dict) -> object:
|
||||
with bpy.data.libraries.load(filepath=data["filepath"], link=True) as (sourceData, targetData):
|
||||
targetData = sourceData
|
||||
return sourceData
|
||||
|
@ -30,14 +30,14 @@ class BlLight(BlDatablock):
|
||||
bl_icon = 'LIGHT_DATA'
|
||||
bl_reload_parent = False
|
||||
|
||||
def construct(self, data):
|
||||
def construct(data: dict) -> object:
|
||||
return bpy.data.lights.new(data["name"], data["type"])
|
||||
|
||||
def _load_implementation(self, data, target):
|
||||
def load(data: dict, datablock: object):
|
||||
loader = Loader()
|
||||
loader.load(target, data)
|
||||
|
||||
def _dump_implementation(self, data, instance=None):
|
||||
def dump(datablock: object) -> dict:
|
||||
assert(instance)
|
||||
dumper = Dumper()
|
||||
dumper.depth = 3
|
||||
|
@ -31,7 +31,7 @@ class BlLightprobe(BlDatablock):
|
||||
bl_icon = 'LIGHTPROBE_GRID'
|
||||
bl_reload_parent = False
|
||||
|
||||
def construct(self, data):
|
||||
def construct(data: dict) -> object:
|
||||
type = 'CUBE' if data['type'] == 'CUBEMAP' else data['type']
|
||||
# See https://developer.blender.org/D6396
|
||||
if bpy.app.version[1] >= 83:
|
||||
@ -39,11 +39,11 @@ class BlLightprobe(BlDatablock):
|
||||
else:
|
||||
logging.warning("Lightprobe replication only supported since 2.83. See https://developer.blender.org/D6396")
|
||||
|
||||
def _load_implementation(self, data, target):
|
||||
def load(data: dict, datablock: object):
|
||||
loader = Loader()
|
||||
loader.load(target, data)
|
||||
|
||||
def _dump_implementation(self, data, instance=None):
|
||||
def dump(datablock: object) -> dict:
|
||||
assert(instance)
|
||||
if bpy.app.version[1] < 83:
|
||||
logging.warning("Lightprobe replication only supported since 2.83. See https://developer.blender.org/D6396")
|
||||
|
@ -24,7 +24,9 @@ import re
|
||||
from uuid import uuid4
|
||||
|
||||
from .dump_anything import Loader, Dumper
|
||||
from .bl_datablock import BlDatablock, get_datablock_from_uuid
|
||||
from .bl_datablock import get_datablock_from_uuid, stamp_uuid
|
||||
from replication.protocol import ReplicatedDatablock
|
||||
from replication.objects import Node
|
||||
|
||||
NODE_SOCKET_INDEX = re.compile('\[(\d*)\]')
|
||||
IGNORED_SOCKETS = ['GEOMETRY', 'SHADER']
|
||||
@ -34,7 +36,7 @@ def load_node(node_data: dict, node_tree: bpy.types.ShaderNodeTree):
|
||||
|
||||
:arg node_data: dumped node data
|
||||
:type node_data: dict
|
||||
:arg node_tree: target node_tree
|
||||
:arg node_tree: datablock node_tree
|
||||
:type node_tree: bpy.types.NodeTree
|
||||
"""
|
||||
loader = Loader()
|
||||
@ -84,7 +86,7 @@ def load_node(node_data: dict, node_tree: bpy.types.ShaderNodeTree):
|
||||
def dump_node(node: bpy.types.ShaderNode) -> dict:
|
||||
""" Dump a single node to a dict
|
||||
|
||||
:arg node: target node
|
||||
:arg node: datablock node
|
||||
:type node: bpy.types.Node
|
||||
:retrun: dict
|
||||
"""
|
||||
@ -241,7 +243,7 @@ def dump_node_tree(node_tree: bpy.types.ShaderNodeTree) -> dict:
|
||||
def dump_node_tree_sockets(sockets: bpy.types.Collection) -> dict:
|
||||
""" dump sockets of a shader_node_tree
|
||||
|
||||
:arg target_node_tree: target node_tree
|
||||
:arg target_node_tree: datablock node_tree
|
||||
:type target_node_tree: bpy.types.NodeTree
|
||||
:arg socket_id: socket identifer
|
||||
:type socket_id: str
|
||||
@ -264,7 +266,7 @@ def load_node_tree_sockets(sockets: bpy.types.Collection,
|
||||
sockets_data: dict):
|
||||
""" load sockets of a shader_node_tree
|
||||
|
||||
:arg target_node_tree: target node_tree
|
||||
:arg target_node_tree: datablock node_tree
|
||||
:type target_node_tree: bpy.types.NodeTree
|
||||
:arg socket_id: socket identifer
|
||||
:type socket_id: str
|
||||
@ -292,7 +294,7 @@ def load_node_tree(node_tree_data: dict, target_node_tree: bpy.types.ShaderNodeT
|
||||
|
||||
:arg node_tree_data: dumped node data
|
||||
:type node_tree_data: dict
|
||||
:arg target_node_tree: target node_tree
|
||||
:arg target_node_tree: datablock node_tree
|
||||
:type target_node_tree: bpy.types.NodeTree
|
||||
"""
|
||||
# TODO: load only required nodes
|
||||
@ -353,7 +355,7 @@ def load_materials_slots(src_materials: list, dst_materials: bpy.types.bpy_prop_
|
||||
|
||||
:arg src_materials: dumped material collection (ex: object.materials)
|
||||
:type src_materials: list of tuples (uuid, name)
|
||||
:arg dst_materials: target material collection pointer
|
||||
:arg dst_materials: datablock material collection pointer
|
||||
:type dst_materials: bpy.types.bpy_prop_collection
|
||||
"""
|
||||
# MATERIAL SLOTS
|
||||
@ -372,36 +374,41 @@ def load_materials_slots(src_materials: list, dst_materials: bpy.types.bpy_prop_
|
||||
dst_materials.append(mat_ref)
|
||||
|
||||
|
||||
class BlMaterial(BlDatablock):
|
||||
class BlMaterial(ReplicatedDatablock):
|
||||
bl_id = "materials"
|
||||
bl_class = bpy.types.Material
|
||||
bl_check_common = False
|
||||
bl_icon = 'MATERIAL_DATA'
|
||||
bl_reload_parent = False
|
||||
|
||||
def construct(self, data):
|
||||
@staticmethod
|
||||
def construct(data: dict) -> object:
|
||||
return bpy.data.materials.new(data["name"])
|
||||
|
||||
def _load_implementation(self, data, target):
|
||||
@staticmethod
|
||||
def load(data: dict, datablock: object):
|
||||
data = data
|
||||
loader = Loader()
|
||||
|
||||
is_grease_pencil = data.get('is_grease_pencil')
|
||||
use_nodes = data.get('use_nodes')
|
||||
|
||||
loader.load(target, data)
|
||||
loader.load(datablock, data)
|
||||
|
||||
if is_grease_pencil:
|
||||
if not target.is_grease_pencil:
|
||||
bpy.data.materials.create_gpencil_data(target)
|
||||
loader.load(target.grease_pencil, data['grease_pencil'])
|
||||
if not datablock.is_grease_pencil:
|
||||
bpy.data.materials.create_gpencil_data(datablock)
|
||||
loader.load(datablock.grease_pencil, data['grease_pencil'])
|
||||
elif use_nodes:
|
||||
if target.node_tree is None:
|
||||
target.use_nodes = True
|
||||
if datablock.node_tree is None:
|
||||
datablock.use_nodes = True
|
||||
|
||||
load_node_tree(data['node_tree'], target.node_tree)
|
||||
load_node_tree(data['node_tree'], datablock.node_tree)
|
||||
|
||||
@staticmethod
|
||||
def dump(datablock: object) -> dict:
|
||||
stamp_uuid(datablock)
|
||||
|
||||
def _dump_implementation(self, data, instance=None):
|
||||
assert(instance)
|
||||
mat_dumper = Dumper()
|
||||
mat_dumper.depth = 2
|
||||
mat_dumper.include_filter = [
|
||||
@ -427,9 +434,9 @@ class BlMaterial(BlDatablock):
|
||||
'line_priority',
|
||||
'is_grease_pencil'
|
||||
]
|
||||
data = mat_dumper.dump(instance)
|
||||
data = mat_dumper.dump(datablock)
|
||||
|
||||
if instance.is_grease_pencil:
|
||||
if datablock.is_grease_pencil:
|
||||
gp_mat_dumper = Dumper()
|
||||
gp_mat_dumper.depth = 3
|
||||
|
||||
@ -463,14 +470,14 @@ class BlMaterial(BlDatablock):
|
||||
'use_overlap_strokes',
|
||||
'use_fill_holdout',
|
||||
]
|
||||
data['grease_pencil'] = gp_mat_dumper.dump(instance.grease_pencil)
|
||||
elif instance.use_nodes:
|
||||
data['node_tree'] = dump_node_tree(instance.node_tree)
|
||||
data['grease_pencil'] = gp_mat_dumper.dump(datablock.grease_pencil)
|
||||
elif datablock.use_nodes:
|
||||
data['node_tree'] = dump_node_tree(datablock.node_tree)
|
||||
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def _resolve_deps_implementation(datablock):
|
||||
def resolve_deps(datablock: object) -> [object]:
|
||||
# TODO: resolve node group deps
|
||||
deps = []
|
||||
|
||||
|
@ -22,12 +22,21 @@ import mathutils
|
||||
import logging
|
||||
import numpy as np
|
||||
|
||||
from .dump_anything import Dumper, Loader, np_load_collection_primitives, np_dump_collection_primitive, np_load_collection, np_dump_collection
|
||||
from .dump_anything import (Dumper,
|
||||
Loader,
|
||||
np_load_collection_primitives,
|
||||
np_dump_collection_primitive,
|
||||
np_load_collection, np_dump_collection)
|
||||
from replication.constants import DIFF_BINARY
|
||||
from replication.exception import ContextError
|
||||
from .bl_datablock import BlDatablock, get_datablock_from_uuid
|
||||
from replication.protocol import ReplicatedDatablock
|
||||
from replication.objects import Node
|
||||
|
||||
from .bl_datablock import get_datablock_from_uuid, stamp_uuid
|
||||
from .bl_material import dump_materials_slots, load_materials_slots
|
||||
|
||||
from ..preferences import get_preferences
|
||||
|
||||
VERTICE = ['co']
|
||||
|
||||
EDGE = [
|
||||
@ -49,80 +58,87 @@ POLYGON = [
|
||||
'material_index',
|
||||
]
|
||||
|
||||
class BlMesh(BlDatablock):
|
||||
|
||||
class BlMesh(ReplicatedDatablock):
|
||||
bl_id = "meshes"
|
||||
bl_class = bpy.types.Mesh
|
||||
bl_check_common = False
|
||||
bl_icon = 'MESH_DATA'
|
||||
bl_reload_parent = True
|
||||
|
||||
def construct(self, data):
|
||||
instance = bpy.data.meshes.new(data["name"])
|
||||
instance.uuid = self.uuid
|
||||
return instance
|
||||
@staticmethod
|
||||
def construct(data: dict) -> object:
|
||||
datablock = bpy.data.meshes.new(data["name"])
|
||||
datablock.uuid = data['uuid']
|
||||
return datablock
|
||||
|
||||
def _load_implementation(self, data, target):
|
||||
if not target or target.is_editmode:
|
||||
@staticmethod
|
||||
def load(data: dict, datablock: object):
|
||||
data = data
|
||||
|
||||
if not datablock or datablock.is_editmode:
|
||||
raise ContextError
|
||||
else:
|
||||
loader = Loader()
|
||||
loader.load(target, data)
|
||||
loader.load(datablock, data)
|
||||
|
||||
# MATERIAL SLOTS
|
||||
src_materials = data.get('materials', None)
|
||||
if src_materials:
|
||||
load_materials_slots(src_materials, target.materials)
|
||||
load_materials_slots(src_materials, datablock.materials)
|
||||
|
||||
# CLEAR GEOMETRY
|
||||
if target.vertices:
|
||||
target.clear_geometry()
|
||||
if datablock.vertices:
|
||||
datablock.clear_geometry()
|
||||
|
||||
target.vertices.add(data["vertex_count"])
|
||||
target.edges.add(data["egdes_count"])
|
||||
target.loops.add(data["loop_count"])
|
||||
target.polygons.add(data["poly_count"])
|
||||
datablock.vertices.add(data["vertex_count"])
|
||||
datablock.edges.add(data["egdes_count"])
|
||||
datablock.loops.add(data["loop_count"])
|
||||
datablock.polygons.add(data["poly_count"])
|
||||
|
||||
# LOADING
|
||||
np_load_collection(data['vertices'], target.vertices, VERTICE)
|
||||
np_load_collection(data['edges'], target.edges, EDGE)
|
||||
np_load_collection(data['loops'], target.loops, LOOP)
|
||||
np_load_collection(data["polygons"],target.polygons, POLYGON)
|
||||
np_load_collection(data['vertices'], datablock.vertices, VERTICE)
|
||||
np_load_collection(data['edges'], datablock.edges, EDGE)
|
||||
np_load_collection(data['loops'], datablock.loops, LOOP)
|
||||
np_load_collection(data["polygons"], datablock.polygons, POLYGON)
|
||||
|
||||
# UV Layers
|
||||
if 'uv_layers' in data.keys():
|
||||
for layer in data['uv_layers']:
|
||||
if layer not in target.uv_layers:
|
||||
target.uv_layers.new(name=layer)
|
||||
if layer not in datablock.uv_layers:
|
||||
datablock.uv_layers.new(name=layer)
|
||||
|
||||
np_load_collection_primitives(
|
||||
target.uv_layers[layer].data,
|
||||
datablock.uv_layers[layer].data,
|
||||
'uv',
|
||||
data["uv_layers"][layer]['data'])
|
||||
|
||||
# Vertex color
|
||||
if 'vertex_colors' in data.keys():
|
||||
for color_layer in data['vertex_colors']:
|
||||
if color_layer not in target.vertex_colors:
|
||||
target.vertex_colors.new(name=color_layer)
|
||||
if color_layer not in datablock.vertex_colors:
|
||||
datablock.vertex_colors.new(name=color_layer)
|
||||
|
||||
np_load_collection_primitives(
|
||||
target.vertex_colors[color_layer].data,
|
||||
datablock.vertex_colors[color_layer].data,
|
||||
'color',
|
||||
data["vertex_colors"][color_layer]['data'])
|
||||
|
||||
target.validate()
|
||||
target.update()
|
||||
datablock.validate()
|
||||
datablock.update()
|
||||
|
||||
def _dump_implementation(self, data, instance=None):
|
||||
assert(instance)
|
||||
@staticmethod
|
||||
def dump(datablock: object) -> dict:
|
||||
stamp_uuid(datablock)
|
||||
|
||||
if (instance.is_editmode or bpy.context.mode == "SCULPT") and not self.preferences.sync_flags.sync_during_editmode:
|
||||
if (datablock.is_editmode or bpy.context.mode == "SCULPT") and not get_preferences().sync_flags.sync_during_editmode:
|
||||
raise ContextError("Mesh is in edit mode")
|
||||
mesh = instance
|
||||
mesh = datablock
|
||||
|
||||
dumper = Dumper()
|
||||
dumper.depth = 1
|
||||
dumper.include_filter = [
|
||||
'uuid'
|
||||
'name',
|
||||
'use_auto_smooth',
|
||||
'auto_smooth_angle',
|
||||
@ -153,21 +169,23 @@ class BlMesh(BlDatablock):
|
||||
data['uv_layers'] = {}
|
||||
for layer in mesh.uv_layers:
|
||||
data['uv_layers'][layer.name] = {}
|
||||
data['uv_layers'][layer.name]['data'] = np_dump_collection_primitive(layer.data, 'uv')
|
||||
data['uv_layers'][layer.name]['data'] = np_dump_collection_primitive(
|
||||
layer.data, 'uv')
|
||||
|
||||
# Vertex color
|
||||
if mesh.vertex_colors:
|
||||
data['vertex_colors'] = {}
|
||||
for color_map in mesh.vertex_colors:
|
||||
data['vertex_colors'][color_map.name] = {}
|
||||
data['vertex_colors'][color_map.name]['data'] = np_dump_collection_primitive(color_map.data, 'color')
|
||||
data['vertex_colors'][color_map.name]['data'] = np_dump_collection_primitive(
|
||||
color_map.data, 'color')
|
||||
|
||||
# Materials
|
||||
data['materials'] = dump_materials_slots(instance.materials)
|
||||
data['materials'] = dump_materials_slots(datablock.materials)
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def _resolve_deps_implementation(datablock):
|
||||
def resolve_deps(datablock: object) -> [object]:
|
||||
deps = []
|
||||
|
||||
for material in datablock.materials:
|
||||
@ -178,7 +196,7 @@ class BlMesh(BlDatablock):
|
||||
|
||||
def diff(self):
|
||||
if 'EDIT' in bpy.context.mode \
|
||||
and not self.preferences.sync_flags.sync_during_editmode:
|
||||
and not get_preferences().sync_flags.sync_during_editmode:
|
||||
return False
|
||||
else:
|
||||
return super().diff()
|
||||
|
@ -69,10 +69,10 @@ class BlMetaball(BlDatablock):
|
||||
bl_icon = 'META_BALL'
|
||||
bl_reload_parent = False
|
||||
|
||||
def construct(self, data):
|
||||
def construct(data: dict) -> object:
|
||||
return bpy.data.metaballs.new(data["name"])
|
||||
|
||||
def _load_implementation(self, data, target):
|
||||
def load(data: dict, datablock: object):
|
||||
loader = Loader()
|
||||
loader.load(target, data)
|
||||
|
||||
@ -83,7 +83,7 @@ class BlMetaball(BlDatablock):
|
||||
|
||||
load_metaball_elements(data['elements'], target.elements)
|
||||
|
||||
def _dump_implementation(self, data, instance=None):
|
||||
def dump(datablock: object) -> dict:
|
||||
assert(instance)
|
||||
dumper = Dumper()
|
||||
dumper.depth = 1
|
||||
|
@ -32,15 +32,15 @@ class BlNodeGroup(BlDatablock):
|
||||
bl_icon = 'NODETREE'
|
||||
bl_reload_parent = False
|
||||
|
||||
def construct(self, data):
|
||||
def construct(data: dict) -> object:
|
||||
return bpy.data.node_groups.new(data["name"], data["type"])
|
||||
|
||||
def _load_implementation(self, data, target):
|
||||
def load(data: dict, datablock: object):
|
||||
load_node_tree(data, target)
|
||||
|
||||
def _dump_implementation(self, data, instance=None):
|
||||
def dump(datablock: object) -> dict:
|
||||
return dump_node_tree(instance)
|
||||
|
||||
@staticmethod
|
||||
def _resolve_deps_implementation(datablock):
|
||||
def resolve_deps(datablock: object) -> [object]:
|
||||
return get_node_tree_dependencies(datablock)
|
@ -21,8 +21,15 @@ import re
|
||||
import bpy
|
||||
import mathutils
|
||||
from replication.exception import ContextError
|
||||
from replication.objects import Node
|
||||
from replication.protocol import ReplicatedDatablock
|
||||
|
||||
from .bl_datablock import BlDatablock, get_datablock_from_uuid
|
||||
from .bl_datablock import get_datablock_from_uuid, stamp_uuid
|
||||
from .bl_action import (load_animation_data,
|
||||
dump_animation_data,
|
||||
get_animation_dependencies)
|
||||
|
||||
from ..preferences import get_preferences
|
||||
from .dump_anything import (
|
||||
Dumper,
|
||||
Loader,
|
||||
@ -30,13 +37,13 @@ from .dump_anything import (
|
||||
np_dump_collection)
|
||||
|
||||
|
||||
|
||||
SKIN_DATA = [
|
||||
'radius',
|
||||
'use_loose',
|
||||
'use_root'
|
||||
]
|
||||
|
||||
|
||||
def get_input_index(e):
|
||||
return int(re.findall('[0-9]+', e)[0])
|
||||
|
||||
@ -95,45 +102,40 @@ def load_pose(target_bone, data):
|
||||
|
||||
|
||||
def find_data_from_name(name=None):
|
||||
instance = None
|
||||
datablock = None
|
||||
if not name:
|
||||
pass
|
||||
elif name in bpy.data.meshes.keys():
|
||||
instance = bpy.data.meshes[name]
|
||||
datablock = bpy.data.meshes[name]
|
||||
elif name in bpy.data.lights.keys():
|
||||
instance = bpy.data.lights[name]
|
||||
datablock = bpy.data.lights[name]
|
||||
elif name in bpy.data.cameras.keys():
|
||||
instance = bpy.data.cameras[name]
|
||||
datablock = bpy.data.cameras[name]
|
||||
elif name in bpy.data.curves.keys():
|
||||
instance = bpy.data.curves[name]
|
||||
datablock = bpy.data.curves[name]
|
||||
elif name in bpy.data.metaballs.keys():
|
||||
instance = bpy.data.metaballs[name]
|
||||
datablock = bpy.data.metaballs[name]
|
||||
elif name in bpy.data.armatures.keys():
|
||||
instance = bpy.data.armatures[name]
|
||||
datablock = bpy.data.armatures[name]
|
||||
elif name in bpy.data.grease_pencils.keys():
|
||||
instance = bpy.data.grease_pencils[name]
|
||||
datablock = bpy.data.grease_pencils[name]
|
||||
elif name in bpy.data.curves.keys():
|
||||
instance = bpy.data.curves[name]
|
||||
datablock = bpy.data.curves[name]
|
||||
elif name in bpy.data.lattices.keys():
|
||||
instance = bpy.data.lattices[name]
|
||||
datablock = bpy.data.lattices[name]
|
||||
elif name in bpy.data.speakers.keys():
|
||||
instance = bpy.data.speakers[name]
|
||||
datablock = bpy.data.speakers[name]
|
||||
elif name in bpy.data.lightprobes.keys():
|
||||
# Only supported since 2.83
|
||||
if bpy.app.version[1] >= 83:
|
||||
instance = bpy.data.lightprobes[name]
|
||||
datablock = bpy.data.lightprobes[name]
|
||||
else:
|
||||
logging.warning(
|
||||
"Lightprobe replication only supported since 2.83. See https://developer.blender.org/D6396")
|
||||
elif bpy.app.version[1] >= 91 and name in bpy.data.volumes.keys():
|
||||
# Only supported since 2.91
|
||||
instance = bpy.data.volumes[name]
|
||||
return instance
|
||||
|
||||
|
||||
def load_data(object, name):
|
||||
logging.info("loading data")
|
||||
pass
|
||||
datablock = bpy.data.volumes[name]
|
||||
return datablock
|
||||
|
||||
|
||||
def _is_editmode(object: bpy.types.Object) -> bool:
|
||||
@ -175,6 +177,7 @@ def find_geometry_nodes(modifiers: bpy.types.bpy_prop_collection) -> [bpy.types.
|
||||
|
||||
return nodes_groups
|
||||
|
||||
|
||||
def dump_vertex_groups(src_object: bpy.types.Object) -> dict:
|
||||
""" Dump object's vertex groups
|
||||
|
||||
@ -219,130 +222,127 @@ def load_vertex_groups(dumped_vertex_groups: dict, target_object: bpy.types.Obje
|
||||
for index, weight in vg['vertices']:
|
||||
vertex_group.add([index], weight, 'REPLACE')
|
||||
|
||||
class BlObject(BlDatablock):
|
||||
|
||||
class BlObject(ReplicatedDatablock):
|
||||
bl_id = "objects"
|
||||
bl_class = bpy.types.Object
|
||||
bl_check_common = False
|
||||
bl_icon = 'OBJECT_DATA'
|
||||
bl_reload_parent = False
|
||||
|
||||
def construct(self, data):
|
||||
instance = None
|
||||
|
||||
if self.is_library:
|
||||
with bpy.data.libraries.load(filepath=bpy.data.libraries[self.data['library']].filepath, link=True) as (sourceData, targetData):
|
||||
targetData.objects = [
|
||||
name for name in sourceData.objects if name == self.data['name']]
|
||||
|
||||
instance = bpy.data.objects[self.data['name']]
|
||||
instance.uuid = self.uuid
|
||||
return instance
|
||||
@staticmethod
|
||||
def construct(data: dict) -> bpy.types.Object:
|
||||
datablock = None
|
||||
|
||||
# TODO: refactoring
|
||||
object_name = data.get("name")
|
||||
data_uuid = data.get("data_uuid")
|
||||
data_id = data.get("data")
|
||||
|
||||
object_uuid = data.get('uuid')
|
||||
object_data = get_datablock_from_uuid(
|
||||
data_uuid,
|
||||
find_data_from_name(data_id),
|
||||
ignore=['images']) # TODO: use resolve_from_id
|
||||
|
||||
if object_data is None and data_uuid:
|
||||
raise Exception(f"Fail to load object {data['name']}({self.uuid})")
|
||||
raise Exception(f"Fail to load object {data['name']}({object_uuid})")
|
||||
|
||||
instance = bpy.data.objects.new(object_name, object_data)
|
||||
instance.uuid = self.uuid
|
||||
datablock = bpy.data.objects.new(object_name, object_data)
|
||||
datablock.uuid = object_uuid
|
||||
|
||||
return instance
|
||||
return datablock
|
||||
|
||||
@staticmethod
|
||||
def load(data: dict, datablock: bpy.types.Object):
|
||||
data = node.data
|
||||
load_animation_data(data, datablock)
|
||||
|
||||
def _load_implementation(self, data, target):
|
||||
loader = Loader()
|
||||
|
||||
data_uuid = data.get("data_uuid")
|
||||
data_id = data.get("data")
|
||||
|
||||
if target.data and (target.data.name != data_id):
|
||||
target.data = get_datablock_from_uuid(
|
||||
if datablock.data and (datablock.data.name != data_id):
|
||||
datablock.data = get_datablock_from_uuid(
|
||||
data_uuid, find_data_from_name(data_id), ignore=['images'])
|
||||
|
||||
# vertex groups
|
||||
vertex_groups = data.get('vertex_groups', None)
|
||||
if vertex_groups:
|
||||
load_vertex_groups(vertex_groups, target)
|
||||
load_vertex_groups(vertex_groups, datablock)
|
||||
|
||||
object_data = target.data
|
||||
object_data = datablock.data
|
||||
|
||||
# SHAPE KEYS
|
||||
if 'shape_keys' in data:
|
||||
target.shape_key_clear()
|
||||
datablock.shape_key_clear()
|
||||
|
||||
# Create keys and load vertices coords
|
||||
for key_block in data['shape_keys']['key_blocks']:
|
||||
key_data = data['shape_keys']['key_blocks'][key_block]
|
||||
target.shape_key_add(name=key_block)
|
||||
datablock.shape_key_add(name=key_block)
|
||||
|
||||
loader.load(
|
||||
target.data.shape_keys.key_blocks[key_block], key_data)
|
||||
datablock.data.shape_keys.key_blocks[key_block], key_data)
|
||||
for vert in key_data['data']:
|
||||
target.data.shape_keys.key_blocks[key_block].data[vert].co = key_data['data'][vert]['co']
|
||||
datablock.data.shape_keys.key_blocks[key_block].data[vert].co = key_data['data'][vert]['co']
|
||||
|
||||
# Load relative key after all
|
||||
for key_block in data['shape_keys']['key_blocks']:
|
||||
reference = data['shape_keys']['key_blocks'][key_block]['relative_key']
|
||||
|
||||
target.data.shape_keys.key_blocks[key_block].relative_key = target.data.shape_keys.key_blocks[reference]
|
||||
datablock.data.shape_keys.key_blocks[key_block].relative_key = datablock.data.shape_keys.key_blocks[reference]
|
||||
|
||||
# Load transformation data
|
||||
loader.load(target, data)
|
||||
loader.load(datablock, data)
|
||||
|
||||
# Object display fields
|
||||
if 'display' in data:
|
||||
loader.load(target.display, data['display'])
|
||||
loader.load(datablock.display, data['display'])
|
||||
|
||||
# Parenting
|
||||
parent_id = data.get('parent_id')
|
||||
if parent_id:
|
||||
parent = bpy.data.objects[parent_id]
|
||||
# Avoid reloading
|
||||
if target.parent != parent and parent is not None:
|
||||
target.parent = parent
|
||||
elif target.parent:
|
||||
target.parent = None
|
||||
if datablock.parent != parent and parent is not None:
|
||||
datablock.parent = parent
|
||||
elif datablock.parent:
|
||||
datablock.parent = None
|
||||
|
||||
# Pose
|
||||
if 'pose' in data:
|
||||
if not target.pose:
|
||||
if not datablock.pose:
|
||||
raise Exception('No pose data yet (Fixed in a near futur)')
|
||||
# Bone groups
|
||||
for bg_name in data['pose']['bone_groups']:
|
||||
bg_data = data['pose']['bone_groups'].get(bg_name)
|
||||
bg_target = target.pose.bone_groups.get(bg_name)
|
||||
bg_datablock = datablock.pose.bone_groups.get(bg_name)
|
||||
|
||||
if not bg_target:
|
||||
bg_target = target.pose.bone_groups.new(name=bg_name)
|
||||
if not bg_datablock:
|
||||
bg_datablock = datablock.pose.bone_groups.new(name=bg_name)
|
||||
|
||||
loader.load(bg_target, bg_data)
|
||||
# target.pose.bone_groups.get
|
||||
loader.load(bg_datablock, bg_data)
|
||||
# datablock.pose.bone_groups.get
|
||||
|
||||
# Bones
|
||||
for bone in data['pose']['bones']:
|
||||
target_bone = target.pose.bones.get(bone)
|
||||
datablock_bone = datablock.pose.bones.get(bone)
|
||||
bone_data = data['pose']['bones'].get(bone)
|
||||
|
||||
if 'constraints' in bone_data.keys():
|
||||
loader.load(target_bone, bone_data['constraints'])
|
||||
loader.load(datablock_bone, bone_data['constraints'])
|
||||
|
||||
load_pose(target_bone, bone_data)
|
||||
load_pose(datablock_bone, bone_data)
|
||||
|
||||
if 'bone_index' in bone_data.keys():
|
||||
target_bone.bone_group = target.pose.bone_group[bone_data['bone_group_index']]
|
||||
datablock_bone.bone_group = datablock.pose.bone_group[bone_data['bone_group_index']]
|
||||
|
||||
# TODO: find another way...
|
||||
if target.empty_display_type == "IMAGE":
|
||||
if datablock.empty_display_type == "IMAGE":
|
||||
img_uuid = data.get('data_uuid')
|
||||
if target.data is None and img_uuid:
|
||||
target.data = get_datablock_from_uuid(img_uuid, None)
|
||||
if datablock.data is None and img_uuid:
|
||||
datablock.data = get_datablock_from_uuid(img_uuid, None)
|
||||
|
||||
if hasattr(object_data, 'skin_vertices') \
|
||||
and object_data.skin_vertices\
|
||||
@ -353,34 +353,43 @@ class BlObject(BlDatablock):
|
||||
skin_data.data,
|
||||
SKIN_DATA)
|
||||
|
||||
if hasattr(target, 'cycles_visibility') \
|
||||
if hasattr(datablock, 'cycles_visibility') \
|
||||
and 'cycles_visibility' in data:
|
||||
loader.load(target.cycles_visibility, data['cycles_visibility'])
|
||||
loader.load(datablock.cycles_visibility, data['cycles_visibility'])
|
||||
|
||||
# TODO: handle geometry nodes input from dump_anything
|
||||
if hasattr(target, 'modifiers'):
|
||||
nodes_modifiers = [mod for mod in target.modifiers if mod.type == 'NODES']
|
||||
if hasattr(datablock, 'modifiers'):
|
||||
nodes_modifiers = [
|
||||
mod for mod in datablock.modifiers if mod.type == 'NODES']
|
||||
for modifier in nodes_modifiers:
|
||||
load_modifier_geometry_node_inputs(data['modifiers'][modifier.name], modifier)
|
||||
load_modifier_geometry_node_inputs(
|
||||
data['modifiers'][modifier.name], modifier)
|
||||
|
||||
transform = data.get('transforms', None)
|
||||
if transform:
|
||||
target.matrix_parent_inverse = mathutils.Matrix(transform['matrix_parent_inverse'])
|
||||
target.matrix_basis = mathutils.Matrix(transform['matrix_basis'])
|
||||
target.matrix_local = mathutils.Matrix(transform['matrix_local'])
|
||||
datablock.matrix_parent_inverse = mathutils.Matrix(
|
||||
transform['matrix_parent_inverse'])
|
||||
datablock.matrix_basis = mathutils.Matrix(
|
||||
transform['matrix_basis'])
|
||||
datablock.matrix_local = mathutils.Matrix(
|
||||
transform['matrix_local'])
|
||||
|
||||
def _dump_implementation(self, data, instance=None):
|
||||
assert(instance)
|
||||
@staticmethod
|
||||
def dump(datablock: bpy.types.Object) -> dict:
|
||||
assert(datablock)
|
||||
|
||||
if _is_editmode(instance):
|
||||
if self.preferences.sync_flags.sync_during_editmode:
|
||||
instance.update_from_editmode()
|
||||
stamp_uuid(datablock)
|
||||
|
||||
if _is_editmode(datablock):
|
||||
if get_preferences().sync_flags.sync_during_editmode:
|
||||
datablock.update_from_editmode()
|
||||
else:
|
||||
raise ContextError("Object is in edit-mode.")
|
||||
|
||||
dumper = Dumper()
|
||||
dumper.depth = 1
|
||||
dumper.include_filter = [
|
||||
"uuid",
|
||||
"name",
|
||||
"rotation_mode",
|
||||
"data",
|
||||
@ -413,30 +422,28 @@ class BlObject(BlDatablock):
|
||||
'type'
|
||||
]
|
||||
|
||||
data = dumper.dump(instance)
|
||||
data = dumper.dump(datablock)
|
||||
|
||||
dumper.include_filter = [
|
||||
'matrix_parent_inverse',
|
||||
'matrix_local',
|
||||
'matrix_basis']
|
||||
data['transforms'] = dumper.dump(instance)
|
||||
data['transforms'] = dumper.dump(datablock)
|
||||
dumper.include_filter = [
|
||||
'show_shadows',
|
||||
]
|
||||
data['display'] = dumper.dump(instance.display)
|
||||
data['display'] = dumper.dump(datablock.display)
|
||||
|
||||
data['data_uuid'] = getattr(instance.data, 'uuid', None)
|
||||
if self.is_library:
|
||||
return data
|
||||
data['data_uuid'] = getattr(datablock.data, 'uuid', None)
|
||||
|
||||
# PARENTING
|
||||
if instance.parent:
|
||||
data['parent_id'] = instance.parent.name
|
||||
if datablock.parent:
|
||||
data['parent_id'] = datablock.parent.name
|
||||
|
||||
# MODIFIERS
|
||||
if hasattr(instance, 'modifiers'):
|
||||
if hasattr(datablock, 'modifiers'):
|
||||
data["modifiers"] = {}
|
||||
modifiers = getattr(instance, 'modifiers', None)
|
||||
modifiers = getattr(datablock, 'modifiers', None)
|
||||
if modifiers:
|
||||
dumper.include_filter = None
|
||||
dumper.depth = 1
|
||||
@ -444,9 +451,10 @@ class BlObject(BlDatablock):
|
||||
data["modifiers"][modifier.name] = dumper.dump(modifier)
|
||||
# hack to dump geometry nodes inputs
|
||||
if modifier.type == 'NODES':
|
||||
dumped_inputs = dump_modifier_geometry_node_inputs(modifier)
|
||||
dumped_inputs = dump_modifier_geometry_node_inputs(
|
||||
modifier)
|
||||
data["modifiers"][modifier.name]['inputs'] = dumped_inputs
|
||||
gp_modifiers = getattr(instance, 'grease_pencil_modifiers', None)
|
||||
gp_modifiers = getattr(datablock, 'grease_pencil_modifiers', None)
|
||||
|
||||
if gp_modifiers:
|
||||
dumper.include_filter = None
|
||||
@ -468,16 +476,16 @@ class BlObject(BlDatablock):
|
||||
gp_mod_data['curve'] = curve_dumper.dump(modifier.curve)
|
||||
|
||||
# CONSTRAINTS
|
||||
if hasattr(instance, 'constraints'):
|
||||
if hasattr(datablock, 'constraints'):
|
||||
dumper.include_filter = None
|
||||
dumper.depth = 3
|
||||
data["constraints"] = dumper.dump(instance.constraints)
|
||||
data["constraints"] = dumper.dump(datablock.constraints)
|
||||
|
||||
# POSE
|
||||
if hasattr(instance, 'pose') and instance.pose:
|
||||
if hasattr(datablock, 'pose') and datablock.pose:
|
||||
# BONES
|
||||
bones = {}
|
||||
for bone in instance.pose.bones:
|
||||
for bone in datablock.pose.bones:
|
||||
bones[bone.name] = {}
|
||||
dumper.depth = 1
|
||||
rotation = 'rotation_quaternion' if bone.rotation_mode == 'QUATERNION' else 'rotation_euler'
|
||||
@ -502,7 +510,7 @@ class BlObject(BlDatablock):
|
||||
|
||||
# GROUPS
|
||||
bone_groups = {}
|
||||
for group in instance.pose.bone_groups:
|
||||
for group in datablock.pose.bone_groups:
|
||||
dumper.depth = 3
|
||||
dumper.include_filter = [
|
||||
'name',
|
||||
@ -511,13 +519,12 @@ class BlObject(BlDatablock):
|
||||
bone_groups[group.name] = dumper.dump(group)
|
||||
data['pose']['bone_groups'] = bone_groups
|
||||
|
||||
|
||||
# VERTEx GROUP
|
||||
if len(instance.vertex_groups) > 0:
|
||||
data['vertex_groups'] = dump_vertex_groups(instance)
|
||||
if len(datablock.vertex_groups) > 0:
|
||||
data['vertex_groups'] = dump_vertex_groups(datablock)
|
||||
|
||||
# SHAPE KEYS
|
||||
object_data = instance.data
|
||||
object_data = datablock.data
|
||||
if hasattr(object_data, 'shape_keys') and object_data.shape_keys:
|
||||
dumper = Dumper()
|
||||
dumper.depth = 2
|
||||
@ -548,11 +555,12 @@ class BlObject(BlDatablock):
|
||||
if hasattr(object_data, 'skin_vertices') and object_data.skin_vertices:
|
||||
skin_vertices = list()
|
||||
for skin_data in object_data.skin_vertices:
|
||||
skin_vertices.append(np_dump_collection(skin_data.data, SKIN_DATA))
|
||||
skin_vertices.append(
|
||||
np_dump_collection(skin_data.data, SKIN_DATA))
|
||||
data['skin_vertices'] = skin_vertices
|
||||
|
||||
# CYCLE SETTINGS
|
||||
if hasattr(instance, 'cycles_visibility'):
|
||||
if hasattr(datablock, 'cycles_visibility'):
|
||||
dumper.include_filter = [
|
||||
'camera',
|
||||
'diffuse',
|
||||
@ -561,25 +569,28 @@ class BlObject(BlDatablock):
|
||||
'scatter',
|
||||
'shadow',
|
||||
]
|
||||
data['cycles_visibility'] = dumper.dump(instance.cycles_visibility)
|
||||
data['cycles_visibility'] = dumper.dump(
|
||||
datablock.cycles_visibility)
|
||||
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def _resolve_deps_implementation(datablock):
|
||||
def resolve_deps(datablock: bpy.types.Object) -> list:
|
||||
deps = []
|
||||
|
||||
# Avoid Empty case
|
||||
if datablock.data:
|
||||
deps.append(datablock.data)
|
||||
|
||||
if datablock.parent :
|
||||
if datablock.parent:
|
||||
deps.append(datablock.parent)
|
||||
|
||||
if datablock.instance_type == 'COLLECTION':
|
||||
# TODO: uuid based
|
||||
deps.append(datablock.instance_collection)
|
||||
|
||||
deps.append(get_animation_dependencies(datablock))
|
||||
|
||||
if datablock.modifiers:
|
||||
deps.extend(find_textures_dependencies(datablock.modifiers))
|
||||
deps.extend(find_geometry_nodes(datablock.modifiers))
|
||||
|
@ -23,14 +23,21 @@ import bpy
|
||||
import mathutils
|
||||
from deepdiff import DeepDiff
|
||||
from replication.constants import DIFF_JSON, MODIFIED
|
||||
from replication.protocol import ReplicatedDatablock
|
||||
from replication.objects import Node
|
||||
|
||||
from ..utils import flush_history
|
||||
from .bl_collection import (dump_collection_children, dump_collection_objects,
|
||||
load_collection_childrens, load_collection_objects,
|
||||
resolve_collection_dependencies)
|
||||
from .bl_datablock import BlDatablock
|
||||
from .bl_action import (load_animation_data,
|
||||
dump_animation_data,
|
||||
get_animation_dependencies)
|
||||
from .bl_datablock import stamp_uuid
|
||||
|
||||
from .bl_file import get_filepath
|
||||
from .dump_anything import Dumper, Loader
|
||||
from ..preferences import get_preferences
|
||||
|
||||
RENDER_SETTINGS = [
|
||||
'dither_intensity',
|
||||
@ -286,12 +293,10 @@ def dump_sequence(sequence: bpy.types.Sequence) -> dict:
|
||||
dumper.depth = 1
|
||||
data = dumper.dump(sequence)
|
||||
|
||||
|
||||
# TODO: Support multiple images
|
||||
if sequence.type == 'IMAGE':
|
||||
data['filenames'] = [e.filename for e in sequence.elements]
|
||||
|
||||
|
||||
# Effect strip inputs
|
||||
input_count = getattr(sequence, 'input_count', None)
|
||||
if input_count:
|
||||
@ -344,14 +349,15 @@ def load_sequence(sequence_data: dict, sequence_editor: bpy.types.SequenceEditor
|
||||
strip_channel,
|
||||
strip_frame_start)
|
||||
# load other images
|
||||
if len(images_name)>1:
|
||||
for img_idx in range(1,len(images_name)):
|
||||
if len(images_name) > 1:
|
||||
for img_idx in range(1, len(images_name)):
|
||||
sequence.elements.append((images_name[img_idx]))
|
||||
else:
|
||||
seq = {}
|
||||
|
||||
for i in range(sequence_data['input_count']):
|
||||
seq[f"seq{i+1}"] = sequence_editor.sequences_all.get(sequence_data.get(f"input_{i+1}", None))
|
||||
seq[f"seq{i+1}"] = sequence_editor.sequences_all.get(
|
||||
sequence_data.get(f"input_{i+1}", None))
|
||||
|
||||
sequence = sequence_editor.sequences.new_effect(name=strip_name,
|
||||
type=strip_type,
|
||||
@ -362,12 +368,12 @@ def load_sequence(sequence_data: dict, sequence_editor: bpy.types.SequenceEditor
|
||||
|
||||
loader = Loader()
|
||||
# TODO: Support filepath updates
|
||||
loader.exclure_filter = ['filepath', 'sound', 'filenames','fps']
|
||||
loader.exclure_filter = ['filepath', 'sound', 'filenames', 'fps']
|
||||
loader.load(sequence, sequence_data)
|
||||
sequence.select = False
|
||||
|
||||
|
||||
class BlScene(BlDatablock):
|
||||
class BlScene(ReplicatedDatablock):
|
||||
is_root = True
|
||||
|
||||
bl_id = "scenes"
|
||||
@ -376,58 +382,60 @@ class BlScene(BlDatablock):
|
||||
bl_icon = 'SCENE_DATA'
|
||||
bl_reload_parent = False
|
||||
|
||||
def construct(self, data):
|
||||
instance = bpy.data.scenes.new(data["name"])
|
||||
instance.uuid = self.uuid
|
||||
@staticmethod
|
||||
def construct(data: dict) -> object:
|
||||
datablock = bpy.data.scenes.new(data["name"])
|
||||
datablock.uuid = data.get("uuid")
|
||||
|
||||
return instance
|
||||
return datablock
|
||||
|
||||
def _load_implementation(self, data, target):
|
||||
@staticmethod
|
||||
def load(data: dict, datablock: object):
|
||||
# Load other meshes metadata
|
||||
loader = Loader()
|
||||
loader.load(target, data)
|
||||
loader.load(datablock, data)
|
||||
|
||||
# Load master collection
|
||||
load_collection_objects(
|
||||
data['collection']['objects'], target.collection)
|
||||
data['collection']['objects'], datablock.collection)
|
||||
load_collection_childrens(
|
||||
data['collection']['children'], target.collection)
|
||||
data['collection']['children'], datablock.collection)
|
||||
|
||||
if 'world' in data.keys():
|
||||
target.world = bpy.data.worlds[data['world']]
|
||||
datablock.world = bpy.data.worlds[data['world']]
|
||||
|
||||
# Annotation
|
||||
if 'grease_pencil' in data.keys():
|
||||
target.grease_pencil = bpy.data.grease_pencils[data['grease_pencil']]
|
||||
datablock.grease_pencil = bpy.data.grease_pencils[data['grease_pencil']]
|
||||
|
||||
if self.preferences.sync_flags.sync_render_settings:
|
||||
if get_preferences().sync_flags.sync_render_settings:
|
||||
if 'eevee' in data.keys():
|
||||
loader.load(target.eevee, data['eevee'])
|
||||
loader.load(datablock.eevee, data['eevee'])
|
||||
|
||||
if 'cycles' in data.keys():
|
||||
loader.load(target.cycles, data['cycles'])
|
||||
loader.load(datablock.cycles, data['cycles'])
|
||||
|
||||
if 'render' in data.keys():
|
||||
loader.load(target.render, data['render'])
|
||||
loader.load(datablock.render, data['render'])
|
||||
|
||||
if 'view_settings' in data.keys():
|
||||
loader.load(target.view_settings, data['view_settings'])
|
||||
if target.view_settings.use_curve_mapping and \
|
||||
loader.load(datablock.view_settings, data['view_settings'])
|
||||
if datablock.view_settings.use_curve_mapping and \
|
||||
'curve_mapping' in data['view_settings']:
|
||||
# TODO: change this ugly fix
|
||||
target.view_settings.curve_mapping.white_level = data[
|
||||
datablock.view_settings.curve_mapping.white_level = data[
|
||||
'view_settings']['curve_mapping']['white_level']
|
||||
target.view_settings.curve_mapping.black_level = data[
|
||||
datablock.view_settings.curve_mapping.black_level = data[
|
||||
'view_settings']['curve_mapping']['black_level']
|
||||
target.view_settings.curve_mapping.update()
|
||||
datablock.view_settings.curve_mapping.update()
|
||||
|
||||
# Sequencer
|
||||
sequences = data.get('sequences')
|
||||
|
||||
if sequences:
|
||||
# Create sequencer data
|
||||
target.sequence_editor_create()
|
||||
vse = target.sequence_editor
|
||||
datablock.sequence_editor_create()
|
||||
vse = datablock.sequence_editor
|
||||
|
||||
# Clear removed sequences
|
||||
for seq in vse.sequences_all:
|
||||
@ -437,15 +445,16 @@ class BlScene(BlDatablock):
|
||||
for seq_name, seq_data in sequences.items():
|
||||
load_sequence(seq_data, vse)
|
||||
# If the sequence is no longer used, clear it
|
||||
elif target.sequence_editor and not sequences:
|
||||
target.sequence_editor_clear()
|
||||
elif datablock.sequence_editor and not sequences:
|
||||
datablock.sequence_editor_clear()
|
||||
|
||||
# FIXME: Find a better way after the replication big refacotoring
|
||||
# Keep other user from deleting collection object by flushing their history
|
||||
flush_history()
|
||||
|
||||
def _dump_implementation(self, data, instance=None):
|
||||
assert(instance)
|
||||
@staticmethod
|
||||
def dump(datablock: object) -> dict:
|
||||
stamp_uuid(datablock)
|
||||
|
||||
# Metadata
|
||||
scene_dumper = Dumper()
|
||||
@ -458,41 +467,43 @@ class BlScene(BlDatablock):
|
||||
'frame_start',
|
||||
'frame_end',
|
||||
'frame_step',
|
||||
'uuid'
|
||||
]
|
||||
if self.preferences.sync_flags.sync_active_camera:
|
||||
if get_preferences().sync_flags.sync_active_camera:
|
||||
scene_dumper.include_filter.append('camera')
|
||||
|
||||
data.update(scene_dumper.dump(instance))
|
||||
data = scene_dumper.dump(datablock)
|
||||
dump_animation_data(datablock, data)
|
||||
|
||||
# Master collection
|
||||
data['collection'] = {}
|
||||
data['collection']['children'] = dump_collection_children(
|
||||
instance.collection)
|
||||
datablock.collection)
|
||||
data['collection']['objects'] = dump_collection_objects(
|
||||
instance.collection)
|
||||
datablock.collection)
|
||||
|
||||
scene_dumper.depth = 1
|
||||
scene_dumper.include_filter = None
|
||||
|
||||
# Render settings
|
||||
if self.preferences.sync_flags.sync_render_settings:
|
||||
if get_preferences().sync_flags.sync_render_settings:
|
||||
scene_dumper.include_filter = RENDER_SETTINGS
|
||||
|
||||
data['render'] = scene_dumper.dump(instance.render)
|
||||
data['render'] = scene_dumper.dump(datablock.render)
|
||||
|
||||
if instance.render.engine == 'BLENDER_EEVEE':
|
||||
if datablock.render.engine == 'BLENDER_EEVEE':
|
||||
scene_dumper.include_filter = EVEE_SETTINGS
|
||||
data['eevee'] = scene_dumper.dump(instance.eevee)
|
||||
elif instance.render.engine == 'CYCLES':
|
||||
data['eevee'] = scene_dumper.dump(datablock.eevee)
|
||||
elif datablock.render.engine == 'CYCLES':
|
||||
scene_dumper.include_filter = CYCLES_SETTINGS
|
||||
data['cycles'] = scene_dumper.dump(instance.cycles)
|
||||
data['cycles'] = scene_dumper.dump(datablock.cycles)
|
||||
|
||||
scene_dumper.include_filter = VIEW_SETTINGS
|
||||
data['view_settings'] = scene_dumper.dump(instance.view_settings)
|
||||
data['view_settings'] = scene_dumper.dump(datablock.view_settings)
|
||||
|
||||
if instance.view_settings.use_curve_mapping:
|
||||
if datablock.view_settings.use_curve_mapping:
|
||||
data['view_settings']['curve_mapping'] = scene_dumper.dump(
|
||||
instance.view_settings.curve_mapping)
|
||||
datablock.view_settings.curve_mapping)
|
||||
scene_dumper.depth = 5
|
||||
scene_dumper.include_filter = [
|
||||
'curves',
|
||||
@ -500,21 +511,20 @@ class BlScene(BlDatablock):
|
||||
'location',
|
||||
]
|
||||
data['view_settings']['curve_mapping']['curves'] = scene_dumper.dump(
|
||||
instance.view_settings.curve_mapping.curves)
|
||||
datablock.view_settings.curve_mapping.curves)
|
||||
|
||||
# Sequence
|
||||
vse = instance.sequence_editor
|
||||
vse = datablock.sequence_editor
|
||||
if vse:
|
||||
dumped_sequences = {}
|
||||
for seq in vse.sequences_all:
|
||||
dumped_sequences[seq.name] = dump_sequence(seq)
|
||||
data['sequences'] = dumped_sequences
|
||||
|
||||
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def _resolve_deps_implementation(datablock):
|
||||
def resolve_deps(datablock: object) -> [object]:
|
||||
deps = []
|
||||
|
||||
# Master Collection
|
||||
@ -547,13 +557,13 @@ class BlScene(BlDatablock):
|
||||
def diff(self):
|
||||
exclude_path = []
|
||||
|
||||
if not self.preferences.sync_flags.sync_render_settings:
|
||||
if not get_preferences().sync_flags.sync_render_settings:
|
||||
exclude_path.append("root['eevee']")
|
||||
exclude_path.append("root['cycles']")
|
||||
exclude_path.append("root['view_settings']")
|
||||
exclude_path.append("root['render']")
|
||||
|
||||
if not self.preferences.sync_flags.sync_active_camera:
|
||||
if not get_preferences().sync_flags.sync_active_camera:
|
||||
exclude_path.append("root['camera']")
|
||||
|
||||
return DeepDiff(self.data, self._dump(instance=self.instance), exclude_paths=exclude_path)
|
||||
return DeepDiff(self.data, self._dump(datablock=self.datablock), exclude_paths=exclude_path)
|
||||
|
@ -34,7 +34,7 @@ class BlSound(BlDatablock):
|
||||
bl_icon = 'SOUND'
|
||||
bl_reload_parent = False
|
||||
|
||||
def construct(self, data):
|
||||
def construct(data: dict) -> object:
|
||||
filename = data.get('filename')
|
||||
|
||||
return bpy.data.sounds.load(get_filepath(filename))
|
||||
@ -58,7 +58,7 @@ class BlSound(BlDatablock):
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _resolve_deps_implementation(datablock):
|
||||
def resolve_deps(datablock: object) -> [object]:
|
||||
deps = []
|
||||
if datablock.filepath and datablock.filepath != '<builtin>':
|
||||
ensure_unpacked(datablock)
|
||||
|
@ -30,14 +30,14 @@ class BlSpeaker(BlDatablock):
|
||||
bl_icon = 'SPEAKER'
|
||||
bl_reload_parent = False
|
||||
|
||||
def _load_implementation(self, data, target):
|
||||
def load(data: dict, datablock: object):
|
||||
loader = Loader()
|
||||
loader.load(target, data)
|
||||
|
||||
def construct(self, data):
|
||||
def construct(data: dict) -> object:
|
||||
return bpy.data.speakers.new(data["name"])
|
||||
|
||||
def _dump_implementation(self, data, instance=None):
|
||||
def dump(datablock: object) -> dict:
|
||||
assert(instance)
|
||||
|
||||
dumper = Dumper()
|
||||
@ -61,7 +61,7 @@ class BlSpeaker(BlDatablock):
|
||||
return dumper.dump(instance)
|
||||
|
||||
@staticmethod
|
||||
def _resolve_deps_implementation(datablock):
|
||||
def resolve_deps(datablock: object) -> [object]:
|
||||
deps = []
|
||||
|
||||
sound = datablock.sound
|
||||
|
@ -30,14 +30,14 @@ class BlTexture(BlDatablock):
|
||||
bl_icon = 'TEXTURE'
|
||||
bl_reload_parent = False
|
||||
|
||||
def _load_implementation(self, data, target):
|
||||
def load(data: dict, datablock: object):
|
||||
loader = Loader()
|
||||
loader.load(target, data)
|
||||
|
||||
def construct(self, data):
|
||||
def construct(data: dict) -> object:
|
||||
return bpy.data.textures.new(data["name"], data["type"])
|
||||
|
||||
def _dump_implementation(self, data, instance=None):
|
||||
def dump(datablock: object) -> dict:
|
||||
assert(instance)
|
||||
|
||||
dumper = Dumper()
|
||||
@ -62,7 +62,7 @@ class BlTexture(BlDatablock):
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def _resolve_deps_implementation(datablock):
|
||||
def resolve_deps(datablock: object) -> [object]:
|
||||
deps = []
|
||||
|
||||
image = getattr(datablock,"image", None)
|
||||
|
@ -31,7 +31,7 @@ class BlVolume(BlDatablock):
|
||||
bl_icon = 'VOLUME_DATA'
|
||||
bl_reload_parent = False
|
||||
|
||||
def _load_implementation(self, data, target):
|
||||
def load(data: dict, datablock: object):
|
||||
loader = Loader()
|
||||
loader.load(target, data)
|
||||
loader.load(target.display, data['display'])
|
||||
@ -41,10 +41,10 @@ class BlVolume(BlDatablock):
|
||||
if src_materials:
|
||||
load_materials_slots(src_materials, target.materials)
|
||||
|
||||
def construct(self, data):
|
||||
def construct(data: dict) -> object:
|
||||
return bpy.data.volumes.new(data["name"])
|
||||
|
||||
def _dump_implementation(self, data, instance=None):
|
||||
def dump(datablock: object) -> dict:
|
||||
assert(instance)
|
||||
|
||||
dumper = Dumper()
|
||||
@ -70,7 +70,7 @@ class BlVolume(BlDatablock):
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def _resolve_deps_implementation(datablock):
|
||||
def resolve_deps(datablock: object) -> [object]:
|
||||
deps = []
|
||||
|
||||
external_vdb = Path(bpy.path.abspath(datablock.filepath))
|
||||
|
@ -33,10 +33,10 @@ class BlWorld(BlDatablock):
|
||||
bl_icon = 'WORLD_DATA'
|
||||
bl_reload_parent = False
|
||||
|
||||
def construct(self, data):
|
||||
def construct(data: dict) -> object:
|
||||
return bpy.data.worlds.new(data["name"])
|
||||
|
||||
def _load_implementation(self, data, target):
|
||||
def load(data: dict, datablock: object):
|
||||
loader = Loader()
|
||||
loader.load(target, data)
|
||||
|
||||
@ -46,7 +46,7 @@ class BlWorld(BlDatablock):
|
||||
|
||||
load_node_tree(data['node_tree'], target.node_tree)
|
||||
|
||||
def _dump_implementation(self, data, instance=None):
|
||||
def dump(datablock: object) -> dict:
|
||||
assert(instance)
|
||||
|
||||
world_dumper = Dumper()
|
||||
@ -63,7 +63,7 @@ class BlWorld(BlDatablock):
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def _resolve_deps_implementation(datablock):
|
||||
def resolve_deps(datablock: object) -> [object]:
|
||||
deps = []
|
||||
|
||||
if datablock.use_nodes:
|
||||
|
Submodule multi_user/libs/replication updated: 63093ecf45...ebfe0e9e85
@ -517,6 +517,8 @@ class SessionProps(bpy.types.PropertyGroup):
|
||||
default=False
|
||||
)
|
||||
|
||||
def get_preferences():
|
||||
return bpy.context.preferences.addons[__package__].preferences
|
||||
|
||||
classes = (
|
||||
SessionUser,
|
||||
|
Reference in New Issue
Block a user