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