diff --git a/README.md b/README.md index de39e39..660c584 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ Currently, not all data-block are supported for replication over the wire. The f | node_groups | ❗ | Material only | | metaball | ✔️ | | | object | ✔️ | | +| textures | ❗ | Supported for modifiers only | | texts | ✔️ | | | scene | ✔️ | | | world | ✔️ | | diff --git a/multi_user/bl_types/__init__.py b/multi_user/bl_types/__init__.py index 07a07f1..f16dd06 100644 --- a/multi_user/bl_types/__init__.py +++ b/multi_user/bl_types/__init__.py @@ -39,7 +39,8 @@ __all__ = [ 'bl_sound', 'bl_file', 'bl_sequencer', - 'bl_node_group' + 'bl_node_group', + 'bl_texture' ] # Order here defines execution order from . import * diff --git a/multi_user/bl_types/bl_object.py b/multi_user/bl_types/bl_object.py index 4d0bc79..c17eacd 100644 --- a/multi_user/bl_types/bl_object.py +++ b/multi_user/bl_types/bl_object.py @@ -35,7 +35,7 @@ def load_pose(target_bone, data): def find_data_from_name(name=None): instance = None if not name: - pass + pass elif name in bpy.data.meshes.keys(): instance = bpy.data.meshes[name] elif name in bpy.data.lights.keys(): @@ -78,6 +78,19 @@ def _is_editmode(object: bpy.types.Object) -> bool: child_data.is_editmode) +def find_textures_dependencies(collection): + """ Check collection + """ + textures = [] + for item in collection: + for attr in dir(item): + inst = getattr(item, attr) + if issubclass(type(inst), bpy.types.Texture) and inst is not None: + textures.append(inst) + + return textures + + class BlObject(BlDatablock): bl_id = "objects" bl_class = bpy.types.Object @@ -105,9 +118,9 @@ class BlObject(BlDatablock): data_id = data.get("data") object_data = get_datablock_from_uuid( - data_uuid, + data_uuid, find_data_from_name(data_id), - ignore=['images']) #TODO: use resolve_from_id + ignore=['images']) # TODO: use resolve_from_id instance = bpy.data.objects.new(object_name, object_data) instance.uuid = self.uuid @@ -120,14 +133,15 @@ class BlObject(BlDatablock): data_id = data.get("data") if target.data and (target.data.name != data_id): - target.data = get_datablock_from_uuid(data_uuid, find_data_from_name(data_id), ignore=['images']) + target.data = get_datablock_from_uuid( + data_uuid, find_data_from_name(data_id), ignore=['images']) # vertex groups if 'vertex_groups' in data: target.vertex_groups.clear() for vg in data['vertex_groups']: - vertex_group=target.vertex_groups.new(name = vg['name']) - point_attr='vertices' if 'vertices' in vg else 'points' + vertex_group = target.vertex_groups.new(name=vg['name']) + point_attr = 'vertices' if 'vertices' in vg else 'points' for vert in vg[point_attr]: vertex_group.add( [vert['index']], vert['weight'], 'REPLACE') @@ -136,12 +150,12 @@ class BlObject(BlDatablock): if 'shape_keys' in data: target.shape_key_clear() - object_data=target.data + object_data = target.data # Create keys and load vertices coords for key_block in data['shape_keys']['key_blocks']: - key_data=data['shape_keys']['key_blocks'][key_block] - target.shape_key_add(name = key_block) + key_data = data['shape_keys']['key_blocks'][key_block] + target.shape_key_add(name=key_block) loader.load( target.data.shape_keys.key_blocks[key_block], key_data) @@ -384,4 +398,7 @@ class BlObject(BlDatablock): # TODO: uuid based deps.append(self.instance.instance_collection) + if self.instance.modifiers: + deps.extend(find_textures_dependencies(self.instance.modifiers)) + return deps diff --git a/multi_user/bl_types/bl_texture.py b/multi_user/bl_types/bl_texture.py new file mode 100644 index 0000000..584cc35 --- /dev/null +++ b/multi_user/bl_types/bl_texture.py @@ -0,0 +1,77 @@ +# ##### BEGIN GPL LICENSE BLOCK ##### +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# ##### END GPL LICENSE BLOCK ##### + + +import bpy +import mathutils + +from .dump_anything import Loader, Dumper +from .bl_datablock import BlDatablock + + +class BlTexture(BlDatablock): + bl_id = "textures" + bl_class = bpy.types.Texture + bl_delay_refresh = 1 + bl_delay_apply = 1 + bl_automatic_push = True + bl_check_common = False + bl_icon = 'TEXTURE' + + def _load_implementation(self, data, target): + loader = Loader() + loader.load(target, data) + + def _construct(self, data): + return bpy.data.textures.new(data["name"], data["type"]) + + def _dump_implementation(self, data, instance=None): + assert(instance) + + dumper = Dumper() + dumper.depth = 1 + dumper.exclude_filter = [ + 'tag', + 'original', + 'user', + 'uuid', + 'is_embedded_data', + 'is_evaluated', + 'name_full' + ] + + data = dumper.dump(instance) + color_ramp = getattr(instance, 'color_ramp', None) + + if color_ramp: + dumper.depth = 4 + data['color_ramp'] = dumper.dump(color_ramp) + + return data + + def _resolve_deps_implementation(self): + # TODO: resolve material + deps = [] + + image = getattr(self.instance,"image", None) + + if image: + deps.append(image) + + return deps + + diff --git a/multi_user/operators.py b/multi_user/operators.py index 5493043..1d992de 100644 --- a/multi_user/operators.py +++ b/multi_user/operators.py @@ -23,6 +23,7 @@ import queue import random import shutil import string +import sys import time from operator import itemgetter from pathlib import Path @@ -38,7 +39,7 @@ from replication.exception import NonAuthorizedOperationError from replication.interface import session from . import bl_types, delayable, environment, ui, utils -from .presence import (SessionStatusWidget, renderer, view3d_find) +from .presence import SessionStatusWidget, renderer, view3d_find background_execution_queue = Queue() delayables = [] @@ -195,9 +196,14 @@ class SessionStartOperator(bpy.types.Operator): timout=type_local_config.bl_delay_apply, target_type=type_module_class)) + if bpy.app.version[1] >= 91: + python_binary_path = sys.executable + else: + python_binary_path = bpy.app.binary_path_python + session.configure( factory=bpy_factory, - python_path=bpy.app.binary_path_python, + python_path=python_binary_path, external_update_handling=use_extern_update) if settings.update_method == 'DEPSGRAPH': diff --git a/scripts/test_addon.py b/scripts/test_addon.py index 21dd17b..2f524dd 100644 --- a/scripts/test_addon.py +++ b/scripts/test_addon.py @@ -13,7 +13,7 @@ def main(): if len(sys.argv) > 2: blender_rev = sys.argv[2] else: - blender_rev = "2.90.0" + blender_rev = "2.91.0" try: exit_val = BAT.test_blender_addon(addon_path=addon, blender_revision=blender_rev) diff --git a/tests/test_bl_types/test_texture.py b/tests/test_bl_types/test_texture.py new file mode 100644 index 0000000..462128b --- /dev/null +++ b/tests/test_bl_types/test_texture.py @@ -0,0 +1,24 @@ +import os + +import pytest +from deepdiff import DeepDiff + +import bpy +import random +from multi_user.bl_types.bl_texture import BlTexture + +TEXTURE_TYPES = ['NONE', 'BLEND', 'CLOUDS', 'DISTORTED_NOISE', 'IMAGE', 'MAGIC', 'MARBLE', 'MUSGRAVE', 'NOISE', 'STUCCI', 'VORONOI', 'WOOD'] + +@pytest.mark.parametrize('texture_type', TEXTURE_TYPES) +def test_texture(clear_blend, texture_type): + datablock = bpy.data.textures.new('test', texture_type) + + implementation = BlTexture() + expected = implementation._dump(datablock) + bpy.data.textures.remove(datablock) + + test = implementation._construct(expected) + implementation._load(expected, test) + result = implementation._dump(test) + + assert not DeepDiff(expected, result)