diff --git a/multi_user/bl_types/__init__.py b/multi_user/bl_types/__init__.py index dd79887..754819d 100644 --- a/multi_user/bl_types/__init__.py +++ b/multi_user/bl_types/__init__.py @@ -43,7 +43,7 @@ __all__ = [ "bl_particle", ] # Order here defines execution order -if bpy.app.version[1] >= 91: +if bpy.app.version >= (2,91,0): __all__.append('bl_volume') from . import * diff --git a/multi_user/bl_types/bl_gpencil.py b/multi_user/bl_types/bl_gpencil.py index 87aa200..8eab87f 100644 --- a/multi_user/bl_types/bl_gpencil.py +++ b/multi_user/bl_types/bl_gpencil.py @@ -53,12 +53,12 @@ STROKE = [ "uv_translation", "vertex_color_fill", ] -if bpy.app.version[1] >= 91: +if bpy.app.version >= (2,91,0): STROKE.append('use_cyclic') else: STROKE.append('draw_cyclic') -if bpy.app.version[1] >= 83: +if bpy.app.version >= (2,83,0): STROKE_POINT.append('vertex_color') def dump_stroke(stroke): diff --git a/multi_user/bl_types/bl_lightprobe.py b/multi_user/bl_types/bl_lightprobe.py index 009a77a..1bdabf6 100644 --- a/multi_user/bl_types/bl_lightprobe.py +++ b/multi_user/bl_types/bl_lightprobe.py @@ -37,7 +37,7 @@ class BlLightprobe(ReplicatedDatablock): def construct(data: dict) -> object: type = 'CUBE' if data['type'] == 'CUBEMAP' else data['type'] # See https://developer.blender.org/D6396 - if bpy.app.version[1] >= 83: + if bpy.app.version >= (2,83,0): return bpy.data.lightprobes.new(data["name"], type) else: logging.warning("Lightprobe replication only supported since 2.83. See https://developer.blender.org/D6396") @@ -49,7 +49,7 @@ class BlLightprobe(ReplicatedDatablock): @staticmethod def dump(datablock: object) -> dict: - if bpy.app.version[1] < 83: + if bpy.app.version < (2,83,0): logging.warning("Lightprobe replication only supported since 2.83. See https://developer.blender.org/D6396") dumper = Dumper() diff --git a/multi_user/bl_types/bl_object.py b/multi_user/bl_types/bl_object.py index bc3cad2..37bb1c2 100644 --- a/multi_user/bl_types/bl_object.py +++ b/multi_user/bl_types/bl_object.py @@ -48,7 +48,7 @@ SHAPEKEY_BLOCK_ATTR = [ ] -if bpy.app.version[1] >= 93: +if bpy.app.version >= (2,93,0): SUPPORTED_GEOMETRY_NODE_PARAMETERS = (int, str, float) else: SUPPORTED_GEOMETRY_NODE_PARAMETERS = (int, str) @@ -56,14 +56,24 @@ else: blender 2.92.") -def get_node_group_inputs(node_group): - inputs = [] +def get_node_group_properties_identifiers(node_group): + props_ids = [] + # Inputs for inpt in node_group.inputs: if inpt.type in IGNORED_SOCKETS: continue else: - inputs.append(inpt) - return inputs + props_ids.append((inpt.identifier, inpt.type)) + + if inpt.type in ['INT', 'VALUE', 'BOOLEAN', 'RGBA', 'VECTOR']: + props_ids.append((f"{inpt.identifier}_attribute_name",'STR')) + props_ids.append((f"{inpt.identifier}_use_attribute", 'BOOL')) + + for outpt in node_group.outputs: + if outpt.type not in IGNORED_SOCKETS and outpt.type in ['INT', 'VALUE', 'BOOLEAN', 'RGBA', 'VECTOR']: + props_ids.append((f"{outpt.identifier}_attribute_name", 'STR')) + + return props_ids # return [inpt.identifer for inpt in node_group.inputs if inpt.type not in IGNORED_SOCKETS] @@ -122,29 +132,35 @@ def load_physics(dumped_settings: dict, target: bpy.types.Object): bpy.ops.rigidbody.constraint_remove({"object": target}) -def dump_modifier_geometry_node_inputs(modifier: bpy.types.Modifier) -> list: +def dump_modifier_geometry_node_props(modifier: bpy.types.Modifier) -> list: """ Dump geometry node modifier input properties :arg modifier: geometry node modifier to dump :type modifier: bpy.type.Modifier """ - dumped_inputs = [] - for inpt in get_node_group_inputs(modifier.node_group): - input_value = modifier[inpt.identifier] + dumped_props = [] + + for prop_value, prop_type in get_node_group_properties_identifiers(modifier.node_group): + try: + prop_value = modifier[prop_value] + except KeyError as e: + logging.error(f"fail to dump geomety node modifier property : {prop_value} ({e})") + else: + dump = None + if isinstance(prop_value, bpy.types.ID): + dump = prop_value.uuid + elif isinstance(prop_value, SUPPORTED_GEOMETRY_NODE_PARAMETERS): + dump = prop_value + elif hasattr(prop_value, 'to_list'): + dump = prop_value.to_list() - dumped_input = None - if isinstance(input_value, bpy.types.ID): - dumped_input = input_value.uuid - elif isinstance(input_value, SUPPORTED_GEOMETRY_NODE_PARAMETERS): - dumped_input = input_value - elif hasattr(input_value, 'to_list'): - dumped_input = input_value.to_list() - dumped_inputs.append(dumped_input) + dumped_props.append((dump, prop_type)) + # logging.info(prop_value) - return dumped_inputs + return dumped_props -def load_modifier_geometry_node_inputs(dumped_modifier: dict, target_modifier: bpy.types.Modifier): +def load_modifier_geometry_node_props(dumped_modifier: dict, target_modifier: bpy.types.Modifier): """ Load geometry node modifier inputs :arg dumped_modifier: source dumped modifier to load @@ -153,17 +169,17 @@ def load_modifier_geometry_node_inputs(dumped_modifier: dict, target_modifier: b :type target_modifier: bpy.type.Modifier """ - for input_index, inpt in enumerate(get_node_group_inputs(target_modifier.node_group)): - dumped_value = dumped_modifier['inputs'][input_index] - input_value = target_modifier[inpt.identifier] - if isinstance(input_value, SUPPORTED_GEOMETRY_NODE_PARAMETERS): - target_modifier[inpt.identifier] = dumped_value - elif hasattr(input_value, 'to_list'): + for input_index, inpt in enumerate(get_node_group_properties_identifiers(target_modifier.node_group)): + dumped_value, dumped_type = dumped_modifier['props'][input_index] + input_value = target_modifier[inpt[0]] + if dumped_type in ['INT', 'VALUE', 'STR']: + logging.info(f"{inpt[0]}/{dumped_value}") + target_modifier[inpt[0]] = dumped_value + elif dumped_type in ['RGBA', 'VECTOR']: for index in range(len(input_value)): input_value[index] = dumped_value[index] - elif inpt.type in ['COLLECTION', 'OBJECT']: - target_modifier[inpt.identifier] = get_datablock_from_uuid( - dumped_value, None) + elif dumped_type in ['COLLECTION', 'OBJECT', 'IMAGE', 'TEXTURE', 'MATERIAL']: + target_modifier[inpt[0]] = get_datablock_from_uuid(dumped_value, None) def load_pose(target_bone, data): @@ -198,12 +214,12 @@ def find_data_from_name(name=None): instance = bpy.data.speakers[name] elif name in bpy.data.lightprobes.keys(): # Only supported since 2.83 - if bpy.app.version[1] >= 83: + if bpy.app.version >= (2,83,0): instance = bpy.data.lightprobes[name] else: logging.warning( "Lightprobe replication only supported since 2.83. See https://developer.blender.org/D6396") - elif bpy.app.version[1] >= 91 and name in bpy.data.volumes.keys(): + elif bpy.app.version >= (2,91,0) and name in bpy.data.volumes.keys(): # Only supported since 2.91 instance = bpy.data.volumes[name] return instance @@ -250,10 +266,11 @@ def find_geometry_nodes_dependencies(modifiers: bpy.types.bpy_prop_collection) - for mod in modifiers: if mod.type == 'NODES' and mod.node_group: dependencies.append(mod.node_group) - # for inpt in get_node_group_inputs(mod.node_group): - # parameter = mod.get(inpt.identifier) - # if parameter and isinstance(parameter, bpy.types.ID): - # dependencies.append(parameter) + for inpt, inpt_type in get_node_group_properties_identifiers(mod.node_group): + inpt_value = mod.get(inpt) + # Avoid to handle 'COLLECTION', 'OBJECT' to avoid circular dependencies + if inpt_type in ['IMAGE', 'TEXTURE', 'MATERIAL'] and inpt_value: + dependencies.append(inpt_value) return dependencies @@ -387,10 +404,7 @@ def dump_modifiers(modifiers: bpy.types.bpy_prop_collection)->dict: dumped_modifier = dumper.dump(modifier) # hack to dump geometry nodes inputs if modifier.type == 'NODES': - dumped_inputs = dump_modifier_geometry_node_inputs( - modifier) - dumped_modifier['inputs'] = dumped_inputs - + dumped_modifier['props'] = dump_modifier_geometry_node_props(modifier) elif modifier.type == 'PARTICLE_SYSTEM': dumper.exclude_filter = [ "is_edited", @@ -455,7 +469,7 @@ def load_modifiers(dumped_modifiers: list, modifiers: bpy.types.bpy_prop_collect loader.load(loaded_modifier, dumped_modifier) if loaded_modifier.type == 'NODES': - load_modifier_geometry_node_inputs(dumped_modifier, loaded_modifier) + load_modifier_geometry_node_props(dumped_modifier, loaded_modifier) elif loaded_modifier.type == 'PARTICLE_SYSTEM': default = loaded_modifier.particle_system.settings dumped_particles = dumped_modifier['particle_system'] diff --git a/multi_user/bl_types/bl_scene.py b/multi_user/bl_types/bl_scene.py index c5c9d00..7d3d82c 100644 --- a/multi_user/bl_types/bl_scene.py +++ b/multi_user/bl_types/bl_scene.py @@ -440,7 +440,7 @@ class BlScene(ReplicatedDatablock): if seq.name not in sequences: vse.sequences.remove(seq) # Load existing sequences - for seq_data in sequences.value(): + for seq_data in sequences.values(): load_sequence(seq_data, vse) # If the sequence is no longer used, clear it elif datablock.sequence_editor and not sequences: diff --git a/multi_user/environment.py b/multi_user/environment.py index dc4b13c..a748cbc 100644 --- a/multi_user/environment.py +++ b/multi_user/environment.py @@ -134,7 +134,7 @@ def install_modules(dependencies: list, python_path: str, install_dir: str): module_can_be_imported(package_name) def register(): - if bpy.app.version[1] >= 91: + if bpy.app.version >= (2,91,0): python_binary_path = sys.executable else: python_binary_path = bpy.app.binary_path_python diff --git a/multi_user/operators.py b/multi_user/operators.py index addd380..741effd 100644 --- a/multi_user/operators.py +++ b/multi_user/operators.py @@ -238,7 +238,7 @@ class SessionConnectOperator(bpy.types.Operator): settings.generate_supported_types() - if bpy.app.version[1] >= 91: + if bpy.app.version >= (2,91,0): python_binary_path = sys.executable else: python_binary_path = bpy.app.binary_path_python @@ -309,7 +309,7 @@ class SessionHostOperator(bpy.types.Operator): settings.generate_supported_types() - if bpy.app.version[1] >= 91: + if bpy.app.version >= (2,91,0): python_binary_path = sys.executable else: python_binary_path = bpy.app.binary_path_python diff --git a/tests/test_bl_types/test_lightprobes.py b/tests/test_bl_types/test_lightprobes.py index e5d27d6..4a5614e 100644 --- a/tests/test_bl_types/test_lightprobes.py +++ b/tests/test_bl_types/test_lightprobes.py @@ -7,7 +7,7 @@ import bpy from multi_user.bl_types.bl_lightprobe import BlLightprobe -@pytest.mark.skipif(bpy.app.version[1] < 83, reason="requires blender 2.83 or higher") +@pytest.mark.skipif(bpy.app.version < (2,83,0), reason="requires blender 2.83 or higher") @pytest.mark.parametrize('lightprobe_type', ['PLANAR','GRID','CUBEMAP']) def test_lightprobes(clear_blend, lightprobe_type): bpy.ops.object.lightprobe_add(type=lightprobe_type)