Compare commits
19 Commits
232-fix-ui
...
235-show-c
Author | SHA1 | Date | |
---|---|---|---|
2910ea654b | |||
5ac61b5348 | |||
189e5c6cf1 | |||
80c81dc934 | |||
563fdb693d | |||
a64eea3cea | |||
03ad7c0066 | |||
d685573834 | |||
0681b53141 | |||
6f02b38b0e | |||
92c773dae9 | |||
f48ade6390 | |||
63c4501b88 | |||
06e21c86ce | |||
e28d3860da | |||
7b247372fb | |||
9d484b00e9 | |||
de9255f71c | |||
99528ea3e0 |
@ -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 *
|
||||
|
@ -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):
|
||||
|
@ -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()
|
||||
|
@ -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 = []
|
||||
|
||||
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)
|
||||
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()
|
||||
|
||||
return dumped_inputs
|
||||
dumped_props.append((dump, prop_type))
|
||||
# logging.info(prop_value)
|
||||
|
||||
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']
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
Submodule multi_user/libs/replication updated: d69f259046...9aa015bd69
@ -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
|
||||
|
@ -374,9 +374,9 @@ class SessionPrefs(bpy.types.AddonPreferences):
|
||||
description="sidebar_advanced_log_expanded",
|
||||
default=False
|
||||
)
|
||||
sidebar_advanced_hosting_expanded: bpy.props.BoolProperty(
|
||||
name="sidebar_advanced_hosting_expanded",
|
||||
description="sidebar_advanced_hosting_expanded",
|
||||
sidebar_advanced_uinfo_expanded: bpy.props.BoolProperty(
|
||||
name="sidebar_advanced_uinfo_expanded",
|
||||
description="sidebar_advanced_uinfo_expanded",
|
||||
default=False
|
||||
)
|
||||
sidebar_advanced_net_expanded: bpy.props.BoolProperty(
|
||||
@ -619,6 +619,11 @@ class SessionUser(bpy.types.PropertyGroup):
|
||||
"""
|
||||
username: bpy.props.StringProperty(name="username")
|
||||
current_frame: bpy.props.IntProperty(name="current_frame")
|
||||
color: bpy.props.FloatVectorProperty(name="color", subtype="COLOR",
|
||||
min=0.0,
|
||||
max=1.0,
|
||||
size=4,
|
||||
default=(1.0, 1.0, 1.0, 1.0))
|
||||
|
||||
|
||||
class SessionProps(bpy.types.PropertyGroup):
|
||||
|
@ -203,6 +203,7 @@ class DynamicRightSelectTimer(Timer):
|
||||
|
||||
for node_id in to_lock:
|
||||
node = session.repository.graph.get(node_id)
|
||||
if node and hasattr(node,'data'):
|
||||
instance_mode = node.data.get('instance_type')
|
||||
if instance_mode and instance_mode == 'COLLECTION':
|
||||
to_lock.remove(node_id)
|
||||
|
149
multi_user/ui.py
149
multi_user/ui.py
@ -62,7 +62,41 @@ def printProgressBar(iteration, total, prefix='', suffix='', decimals=1, length=
|
||||
bar = fill * filledLength + fill_empty * (length - filledLength)
|
||||
return f"{prefix} |{bar}| {iteration}/{total}{suffix}"
|
||||
|
||||
|
||||
def get_mode_icon(mode_name: str) -> str:
|
||||
""" given a mode name retrieve a built-in icon
|
||||
"""
|
||||
mode_icon = "NONE"
|
||||
if mode_name == "OBJECT" :
|
||||
mode_icon = "OBJECT_DATAMODE"
|
||||
elif mode_name == "EDIT_MESH" :
|
||||
mode_icon = "EDITMODE_HLT"
|
||||
elif mode_name == 'EDIT_CURVE':
|
||||
mode_icon = "CURVE_DATA"
|
||||
elif mode_name == 'EDIT_SURFACE':
|
||||
mode_icon = "SURFACE_DATA"
|
||||
elif mode_name == 'EDIT_TEXT':
|
||||
mode_icon = "FILE_FONT"
|
||||
elif mode_name == 'EDIT_ARMATURE':
|
||||
mode_icon = "ARMATURE_DATA"
|
||||
elif mode_name == 'EDIT_METABALL':
|
||||
mode_icon = "META_BALL"
|
||||
elif mode_name == 'EDIT_LATTICE':
|
||||
mode_icon = "LATTICE_DATA"
|
||||
elif mode_name == 'POSE':
|
||||
mode_icon = "POSE_HLT"
|
||||
elif mode_name == 'SCULPT':
|
||||
mode_icon = "SCULPTMODE_HLT"
|
||||
elif mode_name == 'PAINT_WEIGHT':
|
||||
mode_icon = "WPAINT_HLT"
|
||||
elif mode_name == 'PAINT_VERTEX':
|
||||
mode_icon = "VPAINT_HLT"
|
||||
elif mode_name == 'PAINT_TEXTURE':
|
||||
mode_icon = "TPAINT_HLT"
|
||||
elif mode_name == 'PARTICLE':
|
||||
mode_icon = "PARTICLES"
|
||||
elif mode_name == 'PAINT_GPENCIL' or mode_name =='EDIT_GPENCIL' or mode_name =='SCULPT_GPENCIL' or mode_name =='WEIGHT_GPENCIL' or mode_name =='VERTEX_GPENCIL':
|
||||
mode_icon = "GREASEPENCIL"
|
||||
return mode_icon
|
||||
class SESSION_PT_settings(bpy.types.Panel):
|
||||
"""Settings panel"""
|
||||
bl_idname = "MULTIUSER_SETTINGS_PT_panel"
|
||||
@ -267,6 +301,20 @@ class SESSION_PT_advanced_settings(bpy.types.Panel):
|
||||
layout = self.layout
|
||||
settings = get_preferences()
|
||||
|
||||
#ADVANCED USER INFO
|
||||
uinfo_section = layout.row().box()
|
||||
uinfo_section.prop(
|
||||
settings,
|
||||
"sidebar_advanced_uinfo_expanded",
|
||||
text="User Info",
|
||||
icon=get_expanded_icon(settings.sidebar_advanced_uinfo_expanded),
|
||||
emboss=False)
|
||||
if settings.sidebar_advanced_uinfo_expanded:
|
||||
uinfo_section_row = uinfo_section.row()
|
||||
uinfo_section_split = uinfo_section_row.split(factor=0.7, align=True)
|
||||
uinfo_section_split.prop(settings, "username", text="")
|
||||
uinfo_section_split.prop(settings, "client_color", text="")
|
||||
|
||||
#ADVANCED NET
|
||||
net_section = layout.row().box()
|
||||
net_section.prop(
|
||||
@ -361,18 +409,31 @@ class SESSION_PT_user(bpy.types.Panel):
|
||||
online_users)-1 >= selected_user else 0
|
||||
|
||||
#USER LIST
|
||||
row = layout.row()
|
||||
box = row.box()
|
||||
split = box.split(factor=0.35)
|
||||
split.label(text="user")
|
||||
split = split.split(factor=0.3)
|
||||
split.label(text="mode")
|
||||
split.label(text="frame")
|
||||
split.label(text="location")
|
||||
split.label(text="ping")
|
||||
col = layout.column(align=True)
|
||||
row = col.row(align=True)
|
||||
row = row.split(factor=0.35, align=True)
|
||||
|
||||
row = layout.row()
|
||||
layout.template_list("SESSION_UL_users", "", context.window_manager,
|
||||
box = row.box()
|
||||
brow = box.row(align=True)
|
||||
brow.label(text="user")
|
||||
|
||||
row = row.split(factor=0.25, align=True)
|
||||
|
||||
box = row.box()
|
||||
brow = box.row(align=True)
|
||||
brow.label(text="mode")
|
||||
box = row.box()
|
||||
brow = box.row(align=True)
|
||||
brow.label(text="frame")
|
||||
box = row.box()
|
||||
brow = box.row(align=True)
|
||||
brow.label(text="scene")
|
||||
box = row.box()
|
||||
brow = box.row(align=True)
|
||||
brow.label(text="ping")
|
||||
|
||||
row = col.row(align=True)
|
||||
row.template_list("SESSION_UL_users", "", context.window_manager,
|
||||
"online_users", context.window_manager, "user_index")
|
||||
|
||||
#OPERATOR ON USER
|
||||
@ -419,45 +480,32 @@ class SESSION_UL_users(bpy.types.UIList):
|
||||
frame_current = str(metadata.get('frame_current','-'))
|
||||
scene_current = metadata.get('scene_current','-')
|
||||
mode_current = metadata.get('mode_current','-')
|
||||
if mode_current == "OBJECT" :
|
||||
mode_icon = "OBJECT_DATAMODE"
|
||||
elif mode_current == "EDIT_MESH" :
|
||||
mode_icon = "EDITMODE_HLT"
|
||||
elif mode_current == 'EDIT_CURVE':
|
||||
mode_icon = "CURVE_DATA"
|
||||
elif mode_current == 'EDIT_SURFACE':
|
||||
mode_icon = "SURFACE_DATA"
|
||||
elif mode_current == 'EDIT_TEXT':
|
||||
mode_icon = "FILE_FONT"
|
||||
elif mode_current == 'EDIT_ARMATURE':
|
||||
mode_icon = "ARMATURE_DATA"
|
||||
elif mode_current == 'EDIT_METABALL':
|
||||
mode_icon = "META_BALL"
|
||||
elif mode_current == 'EDIT_LATTICE':
|
||||
mode_icon = "LATTICE_DATA"
|
||||
elif mode_current == 'POSE':
|
||||
mode_icon = "POSE_HLT"
|
||||
elif mode_current == 'SCULPT':
|
||||
mode_icon = "SCULPTMODE_HLT"
|
||||
elif mode_current == 'PAINT_WEIGHT':
|
||||
mode_icon = "WPAINT_HLT"
|
||||
elif mode_current == 'PAINT_VERTEX':
|
||||
mode_icon = "VPAINT_HLT"
|
||||
elif mode_current == 'PAINT_TEXTURE':
|
||||
mode_icon = "TPAINT_HLT"
|
||||
elif mode_current == 'PARTICLE':
|
||||
mode_icon = "PARTICLES"
|
||||
elif mode_current == 'PAINT_GPENCIL' or mode_current =='EDIT_GPENCIL' or mode_current =='SCULPT_GPENCIL' or mode_current =='WEIGHT_GPENCIL' or mode_current =='VERTEX_GPENCIL':
|
||||
mode_icon = "GREASEPENCIL"
|
||||
mode_current = metadata.get('mode_current','-')
|
||||
mode_icon = get_mode_icon(mode_current)
|
||||
user_color = metadata.get('color',[1.0,1.0,1.0,1.0])
|
||||
item.color = user_color
|
||||
if user['admin']:
|
||||
status_icon = 'FAKE_USER_ON'
|
||||
split = layout.split(factor=0.35)
|
||||
split.label(text=item.username, icon=status_icon)
|
||||
split = split.split(factor=0.3)
|
||||
split.label(icon=mode_icon)
|
||||
split.label(text=frame_current)
|
||||
split.label(text=scene_current)
|
||||
split.label(text=ping)
|
||||
row = layout.split(factor=0.35, align=True)
|
||||
entry = row.row(align=True)
|
||||
entry.scale_x = 0.05
|
||||
entry.enabled = False
|
||||
entry.prop(item, 'color', text="", event=False, full_event=False)
|
||||
entry.enabled = True
|
||||
entry.scale_x = 1.0
|
||||
entry.label(icon=status_icon, text="")
|
||||
entry.label(text=item.username)
|
||||
|
||||
row = row.split(factor=0.25, align=True)
|
||||
|
||||
entry = row.row()
|
||||
entry.label(icon=mode_icon)
|
||||
entry = row.row()
|
||||
entry.label(text=frame_current)
|
||||
entry = row.row()
|
||||
entry.label(text=scene_current)
|
||||
entry = row.row()
|
||||
entry.label(text=ping)
|
||||
|
||||
def draw_property(context, parent, property_uuid, level=0):
|
||||
settings = get_preferences()
|
||||
@ -633,6 +681,9 @@ class VIEW3D_PT_overlay_session(bpy.types.Panel):
|
||||
pref = get_preferences()
|
||||
layout.active = settings.enable_presence
|
||||
|
||||
row = layout.row()
|
||||
row.prop(settings, "enable_presence",text="Presence Overlay")
|
||||
|
||||
row = layout.row()
|
||||
row.prop(settings, "presence_show_selected",text="Selected Objects")
|
||||
|
||||
|
@ -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)
|
||||
|
Reference in New Issue
Block a user