Change conform behavior, now show individual items.
Add store/restore visibility for items
This commit is contained in:
parent
adc35fac62
commit
0c6523049f
@ -2,7 +2,7 @@ bl_info = {
|
|||||||
"name": "Render Toolbox",
|
"name": "Render Toolbox",
|
||||||
"description": "Perform checks and setup outputs",
|
"description": "Perform checks and setup outputs",
|
||||||
"author": "Samuel Bernou",
|
"author": "Samuel Bernou",
|
||||||
"version": (0, 6, 1),
|
"version": (0, 6, 3),
|
||||||
"blender": (4, 0, 0),
|
"blender": (4, 0, 0),
|
||||||
"location": "View3D",
|
"location": "View3D",
|
||||||
"warning": "",
|
"warning": "",
|
||||||
|
164
fn.py
164
fn.py
@ -337,6 +337,19 @@ def get_view_layer_collection(col, vl_col=None, view_layer=None):
|
|||||||
if c is not None:
|
if c is not None:
|
||||||
return c
|
return c
|
||||||
|
|
||||||
|
## Alternative implementation
|
||||||
|
# def get_view_layer_collection(collection, vl_col=None):
|
||||||
|
# """Find the view layer collection corresponding to a given collection"""
|
||||||
|
# if vl_col is None:
|
||||||
|
# vl_col = bpy.context.view_layer.layer_collection
|
||||||
|
# if vl_col.collection == collection:
|
||||||
|
# return vl_col
|
||||||
|
# for child in vl_col.children:
|
||||||
|
# found = get_view_layer_collection(collection, child)
|
||||||
|
# if found:
|
||||||
|
# return found
|
||||||
|
# return None
|
||||||
|
|
||||||
def get_parents_cols(col, root=None, scene=None, cols=None):
|
def get_parents_cols(col, root=None, scene=None, cols=None):
|
||||||
'''Return a list of parents collections of passed col
|
'''Return a list of parents collections of passed col
|
||||||
root : Pass a collection to search in (recursive)
|
root : Pass a collection to search in (recursive)
|
||||||
@ -544,7 +557,24 @@ def build_path_from_template(template: str,
|
|||||||
|
|
||||||
# endregion
|
# endregion
|
||||||
|
|
||||||
# region visibility states store
|
# region visibility states
|
||||||
|
|
||||||
|
## targets for conformation
|
||||||
|
def get_target_collection(target_name, context):
|
||||||
|
"""Get the target collection based on active or the target name, None if nothing"""
|
||||||
|
if target_name:
|
||||||
|
return next((c for c in context.scene.collection.children_recursive if c.name == target_name), None)
|
||||||
|
else:
|
||||||
|
return context.collection
|
||||||
|
|
||||||
|
def get_target_object(target_name, context):
|
||||||
|
"""Get the target object based on active or the target name, None if nothing"""
|
||||||
|
if target_name:
|
||||||
|
return bpy.context.scene.objects.get(target_name)
|
||||||
|
else:
|
||||||
|
return context.object
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def store_visibility_states(collection=None):
|
def store_visibility_states(collection=None):
|
||||||
"""Store visibility states of objects and collections
|
"""Store visibility states of objects and collections
|
||||||
@ -560,7 +590,7 @@ def store_visibility_states(collection=None):
|
|||||||
'collections': {},
|
'collections': {},
|
||||||
'view_layer_collections': {}
|
'view_layer_collections': {}
|
||||||
}
|
}
|
||||||
|
|
||||||
# Determine target viewlayer collection
|
# Determine target viewlayer collection
|
||||||
if collection is None:
|
if collection is None:
|
||||||
target_vl_collection = bpy.context.view_layer.layer_collection
|
target_vl_collection = bpy.context.view_layer.layer_collection
|
||||||
@ -569,7 +599,7 @@ def store_visibility_states(collection=None):
|
|||||||
if target_vl_collection is None:
|
if target_vl_collection is None:
|
||||||
# Collection not found in current viewlayer
|
# Collection not found in current viewlayer
|
||||||
return visibility_states
|
return visibility_states
|
||||||
|
|
||||||
# Store visibility states by iterating through viewlayer collections
|
# Store visibility states by iterating through viewlayer collections
|
||||||
def store_visibility_recursive(vl_col):
|
def store_visibility_recursive(vl_col):
|
||||||
# Get the actual collection from viewlayer collection
|
# Get the actual collection from viewlayer collection
|
||||||
@ -581,14 +611,13 @@ def store_visibility_states(collection=None):
|
|||||||
'hide_viewport': getattr(col, 'hide_viewport', False),
|
'hide_viewport': getattr(col, 'hide_viewport', False),
|
||||||
'hide_render': getattr(col, 'hide_render', False),
|
'hide_render': getattr(col, 'hide_render', False),
|
||||||
}
|
}
|
||||||
|
|
||||||
# Store viewlayer collection visibility states
|
# Store viewlayer collection visibility states
|
||||||
visibility_states['view_layer_collections'][vl_col.name] = {
|
visibility_states['view_layer_collections'][vl_col.name] = {
|
||||||
'exclude': getattr(vl_col, 'exclude', False),
|
'exclude': getattr(vl_col, 'exclude', False),
|
||||||
'hide_viewport': getattr(vl_col, 'hide_viewport', False),
|
'hide_viewport': getattr(vl_col, 'hide_viewport', False),
|
||||||
'indirect_only': getattr(vl_col, 'indirect_only', False),
|
'indirect_only': getattr(vl_col, 'indirect_only', False),
|
||||||
'holdout': getattr(vl_col, 'indirect_only', False),
|
'holdout': getattr(vl_col, 'indirect_only', False),
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
# Store objects in this collection
|
# Store objects in this collection
|
||||||
@ -600,7 +629,7 @@ def store_visibility_states(collection=None):
|
|||||||
'hide_select': obj.hide_select,
|
'hide_select': obj.hide_select,
|
||||||
'hide_viewlayer': obj.hide_get(),
|
'hide_viewlayer': obj.hide_get(),
|
||||||
}
|
}
|
||||||
|
|
||||||
# Recursively process children
|
# Recursively process children
|
||||||
for child_vl_col in vl_col.children:
|
for child_vl_col in vl_col.children:
|
||||||
store_visibility_recursive(child_vl_col)
|
store_visibility_recursive(child_vl_col)
|
||||||
@ -608,3 +637,126 @@ def store_visibility_states(collection=None):
|
|||||||
store_visibility_recursive(target_vl_collection)
|
store_visibility_recursive(target_vl_collection)
|
||||||
|
|
||||||
return visibility_states
|
return visibility_states
|
||||||
|
|
||||||
|
|
||||||
|
def store_collection_states(collection, context=None):
|
||||||
|
'''Pass a colelction and store visibility state'''
|
||||||
|
context = context or bpy.context
|
||||||
|
|
||||||
|
scene = context.scene
|
||||||
|
outliner_state = scene.get('outliner_state', {})
|
||||||
|
## By name
|
||||||
|
# if collection is None:
|
||||||
|
# collection = next((c for c in context.scene.collection.children_recursive if c.name == target_name), None)
|
||||||
|
state = store_visibility_states(collection)
|
||||||
|
key = collection.name if collection else "ALL"
|
||||||
|
outliner_state[key] = state
|
||||||
|
scene['outliner_state'] = outliner_state
|
||||||
|
# return ({'INFO'}, f"Stored visibility states for '{key}'")
|
||||||
|
|
||||||
|
|
||||||
|
def apply_collection_states(collection, context=None):
|
||||||
|
context = context or bpy.context
|
||||||
|
|
||||||
|
scene = context.scene
|
||||||
|
outliner_state = scene.get('outliner_state', {})
|
||||||
|
if collection is None:
|
||||||
|
# If no collection is passed, apply to all collections
|
||||||
|
collection = context.scene.collection
|
||||||
|
key = "ALL"
|
||||||
|
else:
|
||||||
|
key = collection.name
|
||||||
|
|
||||||
|
state = outliner_state.get(key)
|
||||||
|
if not state:
|
||||||
|
return ({'WARNING'}, f"No stored state for '{key}'")
|
||||||
|
# return {'CANCELLED'}
|
||||||
|
|
||||||
|
for obj_name, obj_state in state['objects'].items():
|
||||||
|
# obj = context.scene.objects.get(obj_name)
|
||||||
|
obj = collection.all_objects.get(obj_name)
|
||||||
|
if obj:
|
||||||
|
obj.hide_viewport = obj_state.get('hide_viewport', False)
|
||||||
|
obj.hide_render = obj_state.get('hide_render', False)
|
||||||
|
obj.hide_select = obj_state.get('hide_select', False)
|
||||||
|
try:
|
||||||
|
obj.hide_set(obj_state.get('hide_viewlayer', False))
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
for col_name, col_state in state['collections'].items():
|
||||||
|
col = next((c for c in context.scene.collection.children_recursive if c.name == col_name), None)
|
||||||
|
if col:
|
||||||
|
col.hide_select = col_state.get('hide_select', False)
|
||||||
|
col.hide_viewport = col_state.get('hide_viewport', False)
|
||||||
|
col.hide_render = col_state.get('hide_render', False)
|
||||||
|
|
||||||
|
for vlcol_name, vlcol_state in state['view_layer_collections'].items():
|
||||||
|
col = next((c for c in context.scene.collection.children_recursive if c.name == vlcol_name), None)
|
||||||
|
vlcol = get_view_layer_collection(col, context.view_layer.layer_collection)
|
||||||
|
if vlcol:
|
||||||
|
vlcol.exclude = vlcol_state.get('exclude', False)
|
||||||
|
vlcol.hide_viewport = vlcol_state.get('hide_viewport', False)
|
||||||
|
vlcol.indirect_only = vlcol_state.get('indirect_only', False)
|
||||||
|
vlcol.holdout = vlcol_state.get('holdout', False)
|
||||||
|
|
||||||
|
## ---
|
||||||
|
|
||||||
|
# # Restore object states
|
||||||
|
# for obj, obj_state in state.get('objects', {}).items():
|
||||||
|
# # obj = bpy.data.objects.get(obj_name)
|
||||||
|
# obj.hide_viewport = obj_state.get('hide_viewport', False)
|
||||||
|
# obj.hide_render = obj_state.get('hide_render', False)
|
||||||
|
# obj.hide_select = obj_state.get('hide_select', False)
|
||||||
|
# try:
|
||||||
|
# obj.hide_set(obj_state.get('hide_viewlayer', False))
|
||||||
|
# except Exception:
|
||||||
|
# pass
|
||||||
|
|
||||||
|
# # Restore collection states
|
||||||
|
# for col, col_state in state.get('collections', {}).items():
|
||||||
|
# # col = bpy.data.collections.get(col_name)
|
||||||
|
# col.hide_select = col_state.get('hide_select', False)
|
||||||
|
# col.hide_viewport = col_state.get('hide_viewport', False)
|
||||||
|
# col.hide_render = col_state.get('hide_render', False)
|
||||||
|
|
||||||
|
# for vlcol, vlcol_state in state.get('view_layer_collections', {}).items():
|
||||||
|
# vlcol.hide_select = vlcol_state.get('hide_select', False)
|
||||||
|
# vlcol.hide_viewport = vlcol_state.get('hide_viewport', False)
|
||||||
|
# vlcol.hide_render = vlcol_state.get('hide_render', False)
|
||||||
|
|
||||||
|
|
||||||
|
### ----
|
||||||
|
|
||||||
|
# Restore view layer collection states
|
||||||
|
# def apply_vl_states(vl_col):
|
||||||
|
# vl_state = state.get('view_layer_collections', {}).get(vl_col.name)
|
||||||
|
# if vl_state:
|
||||||
|
# vl_col.exclude = vl_state.get('exclude', False)
|
||||||
|
# vl_col.hide_viewport = vl_state.get('hide_viewport', False)
|
||||||
|
# vl_col.indirect_only = vl_state.get('indirect_only', False)
|
||||||
|
# vl_col.holdout = vl_state.get('holdout', False)
|
||||||
|
# for child in vl_col.children:
|
||||||
|
# apply_vl_states(child)
|
||||||
|
|
||||||
|
# if collection:
|
||||||
|
# vl_col = get_view_layer_collection(collection)
|
||||||
|
# if vl_col:
|
||||||
|
# apply_vl_states(vl_col)
|
||||||
|
# else:
|
||||||
|
# apply_vl_states(context.view_layer.layer_collection)
|
||||||
|
|
||||||
|
|
||||||
|
def delete_collection_states(collection_name, context=None):
|
||||||
|
"""Delete stored visibility states for a collection or all if None"""
|
||||||
|
context = context or bpy.context
|
||||||
|
|
||||||
|
scene = context.scene
|
||||||
|
outliner_state = scene.get('outliner_state', {})
|
||||||
|
key = collection_name if collection_name else "ALL"
|
||||||
|
if key in outliner_state:
|
||||||
|
del outliner_state[key]
|
||||||
|
scene['outliner_state'] = outliner_state
|
||||||
|
return ({'INFO'}, f"Deleted visibility states for '{key}'")
|
||||||
|
else:
|
||||||
|
return ({'WARNING'}, f"No stored state for '{key}'")
|
@ -4,6 +4,7 @@ from . import (
|
|||||||
outputs_search_and_replace,
|
outputs_search_and_replace,
|
||||||
visibility_conflicts,
|
visibility_conflicts,
|
||||||
simplify_conflicts,
|
simplify_conflicts,
|
||||||
|
store_visibility_states,
|
||||||
conform_collection_hierarchy,
|
conform_collection_hierarchy,
|
||||||
scene_checker,
|
scene_checker,
|
||||||
)
|
)
|
||||||
@ -14,6 +15,7 @@ mods = (
|
|||||||
outputs_search_and_replace,
|
outputs_search_and_replace,
|
||||||
visibility_conflicts,
|
visibility_conflicts,
|
||||||
simplify_conflicts,
|
simplify_conflicts,
|
||||||
|
store_visibility_states,
|
||||||
conform_collection_hierarchy,
|
conform_collection_hierarchy,
|
||||||
scene_checker,
|
scene_checker,
|
||||||
)
|
)
|
||||||
|
@ -5,6 +5,7 @@ from bpy.props import (BoolProperty,
|
|||||||
PointerProperty,
|
PointerProperty,
|
||||||
CollectionProperty,
|
CollectionProperty,
|
||||||
StringProperty)
|
StringProperty)
|
||||||
|
|
||||||
from .. import fn
|
from .. import fn
|
||||||
|
|
||||||
## TODO : handle linked collection / object
|
## TODO : handle linked collection / object
|
||||||
@ -31,368 +32,262 @@ def toggle_viewlayer_hide_state(self, context):
|
|||||||
target_object.hide_set(not target_object.hide_get())
|
target_object.hide_set(not target_object.hide_get())
|
||||||
|
|
||||||
|
|
||||||
|
def outliner_conform(apply=False, context=None):
|
||||||
|
"""conform the outliner hierarchy of a target children based on scene properties"""
|
||||||
|
|
||||||
|
context = context or bpy.context
|
||||||
|
props = context.view_layer.render_toolbox_conform
|
||||||
|
|
||||||
|
## Dict model (with object/collection items in one key, and list of strings for affected properties)
|
||||||
|
# affected_items = {
|
||||||
|
# 'objects': {
|
||||||
|
# obj: ['hide_viewlayer, hide_viewport'],
|
||||||
|
# obj_2: ['hide_viewlayer, hide_select'],
|
||||||
|
# },
|
||||||
|
# 'collections': {
|
||||||
|
# vlc_1 : ['exclude','hide_viewlayer','holdout'],
|
||||||
|
# vlc_2 : ['exclude', 'holdout', 'hide_viewport'],
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
|
||||||
|
hierarchy_type = props.hierarchy_type
|
||||||
|
target_name = props.target_name
|
||||||
|
affect_target = props.affect_target
|
||||||
|
conform_selectability = props.conform_selectability
|
||||||
|
conform_viewlayer = props.conform_viewlayer
|
||||||
|
conform_viewport = props.conform_viewport
|
||||||
|
conform_render = props.conform_render
|
||||||
|
conform_exclude = props.conform_exclude
|
||||||
|
conform_holdout = props.conform_holdout
|
||||||
|
conform_use_indirect = props.conform_use_indirect
|
||||||
|
|
||||||
|
affected_items = {
|
||||||
|
'objects': {},
|
||||||
|
'collections': {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if hierarchy_type == 'COLLECTION':
|
||||||
|
ref_collection = fn.get_target_collection(target_name, context)
|
||||||
|
ref_vlc = fn.get_view_layer_collection(ref_collection)
|
||||||
|
if not ref_vlc:
|
||||||
|
print({'ERROR'}, f"View Layer Collection for '{ref_collection.name}' not found")
|
||||||
|
return
|
||||||
|
# print(f'Conform parameters on collection hierarchy from {ref_vlc.name}')
|
||||||
|
|
||||||
|
if affect_target in {'ALL', 'COLLECTION'}:
|
||||||
|
sub_vlc = fn.get_collection_children_recursive(ref_vlc)
|
||||||
|
for vlc in sub_vlc:
|
||||||
|
col_attrs = []
|
||||||
|
if conform_exclude and vlc.exclude != ref_vlc.exclude:
|
||||||
|
if apply: vlc.exclude = ref_vlc.exclude
|
||||||
|
col_attrs.append("exclude")
|
||||||
|
if conform_viewlayer and vlc.hide_viewport != ref_vlc.hide_viewport:
|
||||||
|
if apply: vlc.hide_viewport = ref_vlc.hide_viewport
|
||||||
|
col_attrs.append("hide_viewlayer")
|
||||||
|
if conform_holdout and vlc.holdout != ref_vlc.holdout:
|
||||||
|
if apply: vlc.holdout = ref_vlc.holdout
|
||||||
|
col_attrs.append("holdout")
|
||||||
|
if conform_use_indirect and vlc.indirect_only != ref_vlc.indirect_only:
|
||||||
|
if apply: vlc.indirect_only = ref_vlc.indirect_only
|
||||||
|
col_attrs.append("indirect_only")
|
||||||
|
if conform_selectability and vlc.collection.hide_select != ref_collection.hide_select:
|
||||||
|
if apply: vlc.collection.hide_select = ref_collection.hide_select
|
||||||
|
col_attrs.append("hide_select")
|
||||||
|
if conform_viewport and vlc.collection.hide_viewport != ref_collection.hide_viewport:
|
||||||
|
if apply: vlc.collection.hide_viewport = ref_collection.hide_viewport
|
||||||
|
col_attrs.append("hide_viewport")
|
||||||
|
if conform_render and vlc.collection.hide_render != ref_collection.hide_render:
|
||||||
|
if apply: vlc.collection.hide_render = ref_collection.hide_render
|
||||||
|
col_attrs.append("hide_render")
|
||||||
|
if col_attrs:
|
||||||
|
affected_items['collections'][vlc] = col_attrs
|
||||||
|
|
||||||
|
if affect_target in {'ALL', 'OBJECT'}:
|
||||||
|
for obj in ref_collection.all_objects:
|
||||||
|
obj_attrs = []
|
||||||
|
if conform_selectability and obj.hide_select != ref_collection.hide_select:
|
||||||
|
if apply: obj.hide_select = ref_collection.hide_select
|
||||||
|
obj_attrs.append("hide_select")
|
||||||
|
if conform_viewlayer and obj.hide_get() != ref_vlc.hide_viewport:
|
||||||
|
if apply: obj.hide_set(ref_vlc.hide_viewport)
|
||||||
|
obj_attrs.append("hide_viewlayer")
|
||||||
|
if conform_viewport and obj.hide_viewport != ref_collection.hide_viewport:
|
||||||
|
if apply: obj.hide_viewport = ref_collection.hide_viewport
|
||||||
|
obj_attrs.append("hide_viewport")
|
||||||
|
if conform_render and obj.hide_render != ref_collection.hide_render:
|
||||||
|
if apply: obj.hide_render = ref_collection.hide_render
|
||||||
|
obj_attrs.append("hide_render")
|
||||||
|
if obj_attrs:
|
||||||
|
affected_items['objects'][obj] = obj_attrs
|
||||||
|
|
||||||
|
else:
|
||||||
|
ref_object = fn.get_target_object(target_name, context)
|
||||||
|
if not ref_object:
|
||||||
|
print({'ERROR'}, "No target object found")
|
||||||
|
return
|
||||||
|
# print(f'Conform parameters on object childrens hierarchy from {ref_object.name}')
|
||||||
|
for obj in ref_object.children_recursive:
|
||||||
|
obj_attrs = []
|
||||||
|
if conform_selectability and obj.hide_select != ref_object.hide_select:
|
||||||
|
if apply: obj.hide_select = ref_object.hide_select
|
||||||
|
obj_attrs.append("hide_select")
|
||||||
|
if conform_viewlayer and obj.hide_get() != ref_object.hide_get():
|
||||||
|
if apply: obj.hide_set(ref_object.hide_get())
|
||||||
|
obj_attrs.append("hide_viewlayer")
|
||||||
|
if conform_viewport and obj.hide_viewport != ref_object.hide_viewport:
|
||||||
|
if apply: obj.hide_viewport = ref_object.hide_viewport
|
||||||
|
obj_attrs.append("hide_viewport")
|
||||||
|
if conform_render and obj.hide_render != ref_object.hide_render:
|
||||||
|
if apply: obj.hide_render = ref_object.hide_render
|
||||||
|
obj_attrs.append("hide_render")
|
||||||
|
if obj_attrs:
|
||||||
|
affected_items['objects'][obj] = obj_attrs
|
||||||
|
|
||||||
|
return affected_items
|
||||||
|
|
||||||
|
|
||||||
class RT_OT_conform_collection_hierarchy(Operator):
|
class RT_OT_conform_collection_hierarchy(Operator):
|
||||||
bl_idname = "rt.conform_collection_hierarchy"
|
bl_idname = "rt.conform_collection_hierarchy"
|
||||||
bl_label = "Conform Collection Hierarchy"
|
bl_label = "Conform Collection Hierarchy"
|
||||||
bl_description = "Chek and conform collection visibility hierarchy settings\
|
bl_description = "Conform collection/object hierarchy state.\
|
||||||
\nCan affect collection, objects or both"
|
\nCan affect collection, objects or both"
|
||||||
bl_options = {"REGISTER", "UNDO"}
|
bl_options = {"REGISTER", "UNDO"}
|
||||||
|
|
||||||
|
|
||||||
hierarchy_type: EnumProperty(
|
|
||||||
name="Hierarchy Type",
|
|
||||||
description="Choose whether to conform object hierarchy or collection hierarchy",
|
|
||||||
items=(
|
|
||||||
('COLLECTION', "Collection Hierarchy", "Conform collection hierarchy"),
|
|
||||||
('OBJECT', "Object Hierarchy", "Conform object hierarchy"),
|
|
||||||
),
|
|
||||||
default='COLLECTION'
|
|
||||||
)
|
|
||||||
|
|
||||||
## Utility prop : expose and control view layer hide state for active object
|
|
||||||
## just used for the update (actual bool value means nothing)
|
|
||||||
## can also use RT_PG_object_visibility from vis_conflict ops
|
|
||||||
## TODO: make an ops for this instead ?
|
|
||||||
active_object_viewlayer_hide : BoolProperty(
|
|
||||||
name="Active Object View Layer Hide",
|
|
||||||
description="show / hide active object in current viewlayer",
|
|
||||||
default=True,
|
|
||||||
update=toggle_viewlayer_hide_state
|
|
||||||
)
|
|
||||||
|
|
||||||
target_name: StringProperty(
|
|
||||||
name="Root Item",
|
|
||||||
description="Collection or object to target", # or object name # (useful for excluded collections)
|
|
||||||
default="",
|
|
||||||
search=name_search_callback
|
|
||||||
## basic collection fetch:
|
|
||||||
# search=lambda self, context, edit_text: [(c.name, '') for c in bpy.context.scene.collection.children_recursive if edit_text.lower() in c.name.lower()]
|
|
||||||
)
|
|
||||||
|
|
||||||
affect_target: EnumProperty(
|
|
||||||
name="Affect Target",
|
|
||||||
description="Choose whether to affect collections, objects, or both",
|
|
||||||
items=[
|
|
||||||
('COLLECTION', "Collection", "Affect collections only"),
|
|
||||||
('OBJECT', "Object", "Affect objects only"),
|
|
||||||
('ALL', "All", "Affect both collections and objects")
|
|
||||||
],
|
|
||||||
default='ALL'
|
|
||||||
)
|
|
||||||
|
|
||||||
## Common object and collection
|
|
||||||
|
|
||||||
conform_selectability: BoolProperty(
|
|
||||||
name="Hide Select State",
|
|
||||||
description="Conform hide select select",
|
|
||||||
default=False
|
|
||||||
)
|
|
||||||
|
|
||||||
conform_viewlayer: BoolProperty(
|
|
||||||
name="Hide in Viewlayer State",
|
|
||||||
description="Conform viewlayer temporary hide",
|
|
||||||
default=True
|
|
||||||
)
|
|
||||||
|
|
||||||
conform_viewport: BoolProperty(
|
|
||||||
name="Disable in Viewports State",
|
|
||||||
description="Conform the monitor icon (global viewport disable)",
|
|
||||||
default=False
|
|
||||||
)
|
|
||||||
|
|
||||||
conform_render: BoolProperty(
|
|
||||||
name="Disable in Renders State",
|
|
||||||
description="Conform the camera icon (render visibility)",
|
|
||||||
default=False
|
|
||||||
)
|
|
||||||
|
|
||||||
## Specific to collections
|
|
||||||
|
|
||||||
conform_exclude: BoolProperty(
|
|
||||||
name="Exclude View Layer State",
|
|
||||||
description="Conform the exclude from view layer",
|
|
||||||
default=True
|
|
||||||
)
|
|
||||||
|
|
||||||
conform_holdout: BoolProperty(
|
|
||||||
name="Holdout State",
|
|
||||||
description="Conform Collection Holdout State",
|
|
||||||
default=True
|
|
||||||
)
|
|
||||||
|
|
||||||
conform_use_indirect: BoolProperty(
|
|
||||||
name="Indirect Only State",
|
|
||||||
description="Conform Collection Indirect Only",
|
|
||||||
default=True
|
|
||||||
)
|
|
||||||
|
|
||||||
def invoke(self, context, event):
|
def invoke(self, context, event):
|
||||||
return context.window_manager.invoke_props_dialog(self, width=400)
|
|
||||||
|
|
||||||
def get_target_collection(self, context):
|
props = context.view_layer.render_toolbox_conform
|
||||||
"""Get the target collection based on active or the target name, None if nothing"""
|
|
||||||
if self.target_name:
|
|
||||||
return next((c for c in context.scene.collection.children_recursive if c.name == self.target_name), None)
|
|
||||||
else:
|
|
||||||
return context.collection
|
|
||||||
|
|
||||||
def get_target_object(self, context):
|
## Check if ready to continue OK
|
||||||
"""Get the target object based on active or the target name, None if nothing"""
|
if props.hierarchy_type == 'COLLECTION':
|
||||||
if self.target_name:
|
ref_collection = fn.get_target_collection(props.target_name, context)
|
||||||
return bpy.context.scene.objects.get(self.target_name)
|
self.ref_item = fn.get_view_layer_collection(ref_collection)
|
||||||
else:
|
if not self.ref_item:
|
||||||
return context.object
|
|
||||||
|
|
||||||
def draw(self, context):
|
|
||||||
layout = self.layout
|
|
||||||
# layout.use_property_split = True
|
|
||||||
|
|
||||||
layout.prop(self, "hierarchy_type", text="Work On") # , expand=True
|
|
||||||
layout.separator()
|
|
||||||
tgt_row = layout.row()
|
|
||||||
layout.prop(self, "target_name", text="Search (Optional)")
|
|
||||||
|
|
||||||
if self.hierarchy_type == 'COLLECTION':
|
|
||||||
ref_collection = self.get_target_collection(context)
|
|
||||||
if not ref_collection or ref_collection == context.scene.collection:
|
|
||||||
layout.label(text="Select a collection or search by name", icon='INFO')
|
|
||||||
if ref_collection == context.scene.collection:
|
|
||||||
layout.label(text="Cannot use the scene collection", icon='ERROR')
|
|
||||||
layout.label(text="An excluded collection collection cannot be active (use search)")
|
|
||||||
return
|
|
||||||
|
|
||||||
if not ref_collection:
|
|
||||||
layout.label(text=f"Error: Collection '{ref_collection.name}' not found", icon='ERROR')
|
|
||||||
return
|
|
||||||
|
|
||||||
ref_vlc = fn.get_view_layer_collection(ref_collection)
|
|
||||||
if not ref_vlc:
|
|
||||||
layout.label(text=f"Error: Viewlayer Collection '{ref_collection.name}' not found", icon='ERROR')
|
|
||||||
return
|
|
||||||
|
|
||||||
# tgt_row = layout.row(align=True)
|
|
||||||
tgt_row.label(text="", icon='TRIA_RIGHT')
|
|
||||||
tgt_row.label(text=ref_collection.name, icon='OUTLINER_COLLECTION')
|
|
||||||
|
|
||||||
layout.prop(self, "affect_target", text="Target Items")
|
|
||||||
layout.separator()
|
|
||||||
## Show current collection state (behave badly when changed, should be tweaked before)
|
|
||||||
# col = layout.column(align=True)
|
|
||||||
# row = col.row(align=True)
|
|
||||||
# row.label(text="Reference Collection State:")
|
|
||||||
# row.prop(ref_vlc, "exclude", text="", emboss=False)
|
|
||||||
# row.prop(ref_collection, "hide_select", text="", emboss=False)
|
|
||||||
# row.prop(ref_vlc, "hide_viewport", text="", emboss=False)
|
|
||||||
# row.prop(ref_collection, "hide_viewport", text="", emboss=False)
|
|
||||||
# row.prop(ref_collection, "hide_render", text="", emboss=False)
|
|
||||||
# row.prop(ref_vlc, "holdout", text="", emboss=False)
|
|
||||||
# row.prop(ref_vlc, "indirect_only", text="", emboss=False)
|
|
||||||
|
|
||||||
col = layout.column(align=True)
|
|
||||||
row = col.row(align=True)
|
|
||||||
row.label(text="Parameter To Conform:")
|
|
||||||
## Same order, greyout unused options
|
|
||||||
collec_row = row.row(align=True)
|
|
||||||
collec_row.prop(self, "conform_exclude", text="", icon='CHECKBOX_DEHLT' if ref_vlc.exclude else 'CHECKBOX_HLT') # Exclude from View Layer
|
|
||||||
collec_row.active = self.affect_target != 'OBJECT'
|
|
||||||
|
|
||||||
## Object and collections
|
|
||||||
row.prop(self, "conform_selectability", text="", icon='RESTRICT_SELECT_ON' if ref_collection.hide_select else 'RESTRICT_SELECT_OFF') # Hide Select
|
|
||||||
row.prop(self, "conform_viewlayer", text="", icon='HIDE_ON' if ref_vlc.hide_viewport else 'HIDE_OFF') # Hide in current viewlayer (eye)
|
|
||||||
row.prop(self, "conform_viewport", text="", icon='RESTRICT_VIEW_ON' if ref_collection.hide_viewport else 'RESTRICT_VIEW_OFF') # Disable in Viewports
|
|
||||||
row.prop(self, "conform_render", text="", icon='RESTRICT_RENDER_ON' if ref_collection.hide_render else 'RESTRICT_RENDER_OFF') # Disable in Renders
|
|
||||||
|
|
||||||
## Specific to collections
|
|
||||||
collec_row = row.row(align=True)
|
|
||||||
collec_row.prop(self, "conform_holdout", text="", icon='HOLDOUT_OFF') # Holdout
|
|
||||||
collec_row.prop(self, "conform_use_indirect", text="", icon='INDIRECT_ONLY_OFF') # Indirect Only
|
|
||||||
collec_row.active = self.affect_target != 'OBJECT'
|
|
||||||
|
|
||||||
else:
|
|
||||||
ref_obj = self.get_target_object(context)
|
|
||||||
|
|
||||||
if not ref_obj:
|
|
||||||
layout.label(text="Make object active or search by name", icon='INFO')
|
|
||||||
return
|
|
||||||
|
|
||||||
# tgt_row = layout.row(align=True)
|
|
||||||
tgt_row.label(text="", icon='TRIA_RIGHT')
|
|
||||||
tgt_row.label(text=ref_obj.name, icon='OBJECT_DATA')
|
|
||||||
|
|
||||||
if not ref_obj.children_recursive:
|
|
||||||
layout.label(text="Object has no children", icon='ERROR')
|
|
||||||
return
|
|
||||||
|
|
||||||
layout.separator()
|
|
||||||
## Show current collection state (can behave badly when changed, should be tweaked before)
|
|
||||||
# col = layout.column(align=True)
|
|
||||||
# row = col.row(align=True)
|
|
||||||
# row.label(text="Reference Object State:")
|
|
||||||
# row.prop(ref_obj, "hide_select", text="", emboss=False)
|
|
||||||
# row.prop(self, "active_object_viewlayer_hide", text="", icon='HIDE_ON' if ref_obj.hide_get() else 'HIDE_OFF', emboss=False) # hack
|
|
||||||
# row.prop(ref_obj, "hide_viewport", text="", emboss=False)
|
|
||||||
# row.prop(ref_obj, "hide_render", text="", emboss=False)
|
|
||||||
|
|
||||||
col = layout.column(align=True)
|
|
||||||
row = col.row(align=True)
|
|
||||||
row.label(text="Parameter To Conform:")
|
|
||||||
row.prop(self, "conform_selectability", text="", icon='RESTRICT_SELECT_ON' if ref_obj.hide_select else 'RESTRICT_SELECT_OFF') # Hide Select
|
|
||||||
row.prop(self, "conform_viewlayer", text="", icon='HIDE_ON' if ref_obj.hide_get() else 'HIDE_OFF') # Hide in current viewlayer (eye)
|
|
||||||
row.prop(self, "conform_viewport", text="", icon='RESTRICT_VIEW_ON' if ref_obj.hide_viewport else 'RESTRICT_VIEW_OFF') # Disable in Viewports
|
|
||||||
row.prop(self, "conform_render", text="", icon='RESTRICT_RENDER_ON' if ref_obj.hide_render else 'RESTRICT_RENDER_OFF') # Disable in Renders
|
|
||||||
|
|
||||||
|
|
||||||
## Show dynamically wich object/collection are affected by the current confo.
|
|
||||||
# if self.hierarchy_type == 'COLLECTION':
|
|
||||||
|
|
||||||
# to_conform_viewlayer = {}
|
|
||||||
# to_conform_collection = {}
|
|
||||||
|
|
||||||
# vl_name_list = ['exclude', 'hide_viewport', 'holdout', 'indirect_only']
|
|
||||||
# collec_name_list = ['hide_select', 'hide_viewport', 'hide_render']
|
|
||||||
# ## VL props
|
|
||||||
# if self.conform_exclude:
|
|
||||||
# to_conform_viewlayer['exclude'] = ref_vlc.exclude
|
|
||||||
# if self.conform_viewlayer:
|
|
||||||
# to_conform_viewlayer['hide_viewport'] = ref_vlc.hide_viewport
|
|
||||||
# if self.conform_holdout:
|
|
||||||
# to_conform_viewlayer['holdout'] = ref_vlc.holdout
|
|
||||||
# if self.conform_use_indirect:
|
|
||||||
# to_conform_viewlayer['indirect_only'] = ref_vlc.indirect_only
|
|
||||||
|
|
||||||
# ## collection props
|
|
||||||
# if self.conform_selectability:
|
|
||||||
# to_conform_collection['hide_select'] = ref_vlc.collection.hide_select
|
|
||||||
# if self.conform_viewport:
|
|
||||||
# to_conform_collection['hide_viewport'] = ref_vlc.collection.hide_viewport
|
|
||||||
# if self.conform_render:
|
|
||||||
# to_conform_collection['hide_render'] = ref_vlc.collection.hide_render
|
|
||||||
|
|
||||||
|
|
||||||
# col = layout.column(align=True)
|
|
||||||
# sub_vlc = fn.get_collection_children_recursive(ref_vlc)
|
|
||||||
# for vlc in sub_vlc:
|
|
||||||
# viewlayer_conflicts = [attr for attr, value in to_conform_viewlayer.items() if value != getattr(vlc, attr, None)]
|
|
||||||
# collection_conflicts = [attr for attr, value in to_conform_collection.items() if value != getattr(vlc.collection, attr, None)]
|
|
||||||
|
|
||||||
# if viewlayer_conflicts or collection_conflicts:
|
|
||||||
# row = col.row(align=True)
|
|
||||||
# row.label(text=f"{vlc.name}:", icon='OUTLINER_COLLECTION')
|
|
||||||
# for attr in vl_name_list:
|
|
||||||
# if attr in viewlayer_conflicts:
|
|
||||||
# row.prop(vlc, attr, text="", emboss=False)
|
|
||||||
# else:
|
|
||||||
# row.label(text="", icon='BLANK1')
|
|
||||||
# # subrow = row.row(align=True)
|
|
||||||
# # subrow.prop(vlc, attr, text="", emboss=False)
|
|
||||||
# # subrow.enabled = False
|
|
||||||
# for attr in collec_name_list:
|
|
||||||
# if attr in collection_conflicts:
|
|
||||||
# row.prop(vlc.collection, attr, text="", emboss=False)
|
|
||||||
# else:
|
|
||||||
# row.label(text="", icon='BLANK1')
|
|
||||||
|
|
||||||
|
|
||||||
def execute(self, context):
|
|
||||||
affected_items = [] # List to log affected items
|
|
||||||
if self.hierarchy_type == 'COLLECTION':
|
|
||||||
ref_collection = self.get_target_collection(context)
|
|
||||||
ref_vlc = fn.get_view_layer_collection(ref_collection)
|
|
||||||
if not ref_vlc:
|
|
||||||
self.report({'ERROR'}, f"View Layer Collection for '{ref_collection.name}' not found")
|
self.report({'ERROR'}, f"View Layer Collection for '{ref_collection.name}' not found")
|
||||||
return {'CANCELLED'}
|
return {'CANCELLED'}
|
||||||
print(f'Conform parameters on collection hierarchy from {ref_vlc.name}')
|
fn.store_collection_states(ref_collection, context=context)
|
||||||
|
|
||||||
if self.affect_target in {'ALL', 'COLLECTION'}:
|
|
||||||
## Apply on collection
|
|
||||||
sub_vlc = fn.get_collection_children_recursive(ref_vlc)
|
|
||||||
for vlc in sub_vlc:
|
|
||||||
# Apply view layer collection properties
|
|
||||||
if self.conform_exclude and vlc.exclude != ref_vlc.exclude:
|
|
||||||
vlc.exclude = ref_vlc.exclude
|
|
||||||
affected_items.append((vlc.name, "exclude", ref_vlc.exclude))
|
|
||||||
|
|
||||||
if self.conform_viewlayer and vlc.hide_viewport != ref_vlc.hide_viewport:
|
|
||||||
vlc.hide_viewport = ref_vlc.hide_viewport
|
|
||||||
affected_items.append((vlc.name, "hide_viewport", ref_vlc.hide_viewport))
|
|
||||||
|
|
||||||
if self.conform_holdout and vlc.holdout != ref_vlc.holdout:
|
|
||||||
vlc.holdout = ref_vlc.holdout
|
|
||||||
affected_items.append((vlc.name, "holdout", ref_vlc.holdout))
|
|
||||||
|
|
||||||
if self.conform_use_indirect and vlc.indirect_only != ref_vlc.indirect_only:
|
|
||||||
vlc.indirect_only = ref_vlc.indirect_only
|
|
||||||
affected_items.append((vlc.name, "indirect_only", ref_vlc.indirect_only))
|
|
||||||
|
|
||||||
|
|
||||||
# Apply collection properties
|
|
||||||
if self.conform_selectability and vlc.collection.hide_select != ref_collection.hide_select:
|
|
||||||
vlc.collection.hide_select = ref_collection.hide_select
|
|
||||||
affected_items.append((vlc.collection.name, "hide_select", ref_collection.hide_select))
|
|
||||||
|
|
||||||
if self.conform_viewport and vlc.collection.hide_viewport != ref_collection.hide_viewport:
|
|
||||||
vlc.collection.hide_viewport = ref_collection.hide_viewport
|
|
||||||
affected_items.append((vlc.collection.name, "hide_viewport", ref_collection.hide_viewport))
|
|
||||||
|
|
||||||
if self.conform_render and vlc.collection.hide_render != ref_collection.hide_render:
|
|
||||||
vlc.collection.hide_render = ref_collection.hide_render
|
|
||||||
affected_items.append((vlc.collection.name, "hide_render", ref_collection.hide_render))
|
|
||||||
|
|
||||||
|
|
||||||
if self.affect_target in {'ALL', 'OBJECT'}:
|
|
||||||
## Apply on objects
|
|
||||||
for obj in ref_collection.all_objects:
|
|
||||||
# Apply object properties
|
|
||||||
if self.conform_selectability and obj.hide_select != ref_collection.hide_select:
|
|
||||||
obj.hide_select = ref_collection.hide_select
|
|
||||||
affected_items.append((obj.name, "hide_select", ref_collection.hide_select))
|
|
||||||
|
|
||||||
if self.conform_viewlayer and obj.hide_get() != ref_vlc.hide_viewport:
|
|
||||||
obj.hide_set(ref_vlc.hide_viewport)
|
|
||||||
affected_items.append((obj.name, "hide_viewlayer", ref_vlc.hide_viewport))
|
|
||||||
|
|
||||||
if self.conform_viewport and obj.hide_viewport != ref_collection.hide_viewport:
|
|
||||||
obj.hide_viewport = ref_collection.hide_viewport
|
|
||||||
affected_items.append((obj.name, "hide_viewport", ref_collection.hide_viewport))
|
|
||||||
|
|
||||||
if self.conform_render and obj.hide_render != ref_collection.hide_render:
|
|
||||||
obj.hide_render = ref_collection.hide_render
|
|
||||||
affected_items.append((obj.name, "hide_render", ref_collection.hide_render))
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
ref_object = self.get_target_object(context)
|
self.ref_item = fn.get_target_object(props.target_name, context)
|
||||||
if not ref_object:
|
if not self.ref_item:
|
||||||
self.report({'ERROR'}, "No target object found")
|
self.report({'ERROR'}, "No target object found")
|
||||||
return {'CANCELLED'}
|
return {'CANCELLED'}
|
||||||
print(f'Conform parameters on object childrens hierarchy from {ref_object.name}')
|
|
||||||
for obj in ref_object.children_recursive:
|
|
||||||
# Apply object properties
|
|
||||||
if self.conform_selectability and obj.hide_select != ref_object.hide_select:
|
|
||||||
obj.hide_select = ref_object.hide_select
|
|
||||||
affected_items.append((obj.name, "hide_select", ref_object.hide_select))
|
|
||||||
|
|
||||||
if self.conform_viewlayer and obj.hide_get() != ref_object.hide_get():
|
self.affected_items_dict = outliner_conform(apply=False, context=context)
|
||||||
obj.hide_set(ref_object.hide_get())
|
|
||||||
affected_items.append((obj.name, "hide_viewlayer", ref_object.hide_get()))
|
|
||||||
|
|
||||||
if self.conform_viewport and obj.hide_viewport != ref_object.hide_viewport:
|
## Merge collection and viewlauer colleciton on the same keys by name
|
||||||
obj.hide_viewport = ref_object.hide_viewport
|
# merged_collection_dict = {}
|
||||||
affected_items.append((obj.name, "hide_viewport", ref_object.hide_viewport))
|
# for k, v in self.affected_items_dict.items():
|
||||||
|
# if isinstance(k, bpy.types.Object):
|
||||||
|
# merged_collection_dict[k] = v
|
||||||
|
# else:
|
||||||
|
# if k.name not in merged_collection_dict.keys():
|
||||||
|
# merged_collection_dict[k.name] = []
|
||||||
|
# merged_collection_dict[k.name].extend(v)
|
||||||
|
|
||||||
if self.conform_render and obj.hide_render != ref_object.hide_render:
|
# self.affected_items_dict = merged_collection_dict
|
||||||
obj.hide_render = ref_object.hide_render
|
|
||||||
affected_items.append((obj.name, "hide_render", ref_object.hide_render))
|
|
||||||
|
|
||||||
# Log the affected items
|
return context.window_manager.invoke_props_dialog(self, width=400)
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
|
||||||
|
layout = self.layout
|
||||||
|
# show non conform items
|
||||||
|
props = context.view_layer.render_toolbox_conform
|
||||||
|
|
||||||
|
layout.label(text=f"Conform Hierarchy from '{self.ref_item.name}'", icon='OUTLINER_COLLECTION' if props.hierarchy_type == 'COLLECTION' else 'OBJECT_DATA')
|
||||||
|
## show conform parameters
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.enabled = False # don't want to mes with source item here ! TODO : convert to labels
|
||||||
|
row.label(text="Conforming:")
|
||||||
|
if props.hierarchy_type == 'COLLECTION':
|
||||||
|
if props.conform_exclude:
|
||||||
|
row.prop(self.ref_item, 'exclude', text="", emboss=False)
|
||||||
|
if props.conform_viewlayer:
|
||||||
|
row.prop(self.ref_item, 'hide_viewport', text="", emboss=False)
|
||||||
|
if props.conform_holdout:
|
||||||
|
row.prop(self.ref_item, 'holdout', text="", emboss=False)
|
||||||
|
if props.conform_use_indirect:
|
||||||
|
row.prop(self.ref_item, 'indirect_only', text="", emboss=False)
|
||||||
|
## collection props
|
||||||
|
if props.conform_selectability:
|
||||||
|
row.prop(self.ref_item.collection, 'hide_select', text="", emboss=False)
|
||||||
|
if props.conform_viewport:
|
||||||
|
row.prop(self.ref_item.collection, 'hide_viewport', text="", emboss=False)
|
||||||
|
if props.conform_render:
|
||||||
|
row.prop(self.ref_item.collection, 'hide_render', text="", emboss=False)
|
||||||
|
else:
|
||||||
|
## Object
|
||||||
|
if props.conform_selectability:
|
||||||
|
row.prop(self.ref_item, 'hide_select', text="", emboss=False)
|
||||||
|
if props.conform_viewlayer:
|
||||||
|
row.label(text="", icon='HIDE_ON' if self.ref_item.hide_get() else 'HIDE_OFF')
|
||||||
|
if props.conform_viewport:
|
||||||
|
row.prop(self.ref_item, 'hide_viewport', text="", emboss=False)
|
||||||
|
if props.conform_render:
|
||||||
|
row.prop(self.ref_item, 'hide_render', text="", emboss=False)
|
||||||
|
|
||||||
|
|
||||||
|
layout.separator()
|
||||||
|
|
||||||
|
outliner_state = context.scene.get('outliner_state', {})
|
||||||
|
## Should have been saved at invoke
|
||||||
|
if outliner_state.get(self.ref_item.name):
|
||||||
|
# layout.label(text=f"Stored visibility states for '{self.ref_item.name}'", icon='FILE_TICK')
|
||||||
|
layout.operator("rt.apply_visibility_states", text=f"Restore State: {self.ref_item.name}", icon='FILE_REFRESH').collection_name = self.ref_item.name
|
||||||
|
|
||||||
|
layout.separator()
|
||||||
|
# vl_attr_list = ['exclude', 'hide_viewlayer', 'holdout', 'indirect_only']
|
||||||
|
attr_list = ['hide_select', 'hide_viewport', 'hide_render']
|
||||||
|
full_attr_list = ['exclude', 'hide_select', 'hide_viewlayer', 'hide_viewport', 'hide_render', 'holdout', 'indirect_only']
|
||||||
|
|
||||||
|
for item_type, type_dict in self.affected_items_dict.items():
|
||||||
|
|
||||||
|
for k, v in type_dict.items():
|
||||||
|
if item_type == "objects":
|
||||||
|
icon = f'OUTLINER_OB_{k.type}'
|
||||||
|
name = k.name
|
||||||
|
else:
|
||||||
|
icon = 'OUTLINER_COLLECTION'
|
||||||
|
name = k.name
|
||||||
|
|
||||||
|
row = layout.row(align=True)
|
||||||
|
row.label(text=name, icon=icon)
|
||||||
|
|
||||||
|
for attr in full_attr_list:
|
||||||
|
if attr in v:
|
||||||
|
if attr == 'hide_viewlayer':
|
||||||
|
## Special case for view_layer hide (hide_viewport for layer collection -- hide_get for object)
|
||||||
|
if item_type == "objects":
|
||||||
|
## TODO: create a dynamic property to toggle object VL viz
|
||||||
|
row.label(text="", icon='HIDE_ON' if k.hide_get() else 'HIDE_OFF') # , emboss=False, toggle=True
|
||||||
|
else:
|
||||||
|
row.prop(k, 'hide_viewport', text="", emboss=False)
|
||||||
|
# row.prop(k, 'hide_viewport', text="", icon='HIDE_ON' if k.hide_viewport else 'HIDE_OFF', emboss=False) # , toggle=True
|
||||||
|
|
||||||
|
|
||||||
|
elif attr in attr_list and item_type == "collections":
|
||||||
|
## accessed on collection object from vl_col
|
||||||
|
row.prop(k.collection, attr, text="", emboss=False)
|
||||||
|
else:
|
||||||
|
row.prop(k, attr, text="", emboss=False)
|
||||||
|
|
||||||
|
else:
|
||||||
|
row.label(text="", icon='BLANK1')
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
# props = context.scene.render_toolbox_conform
|
||||||
|
|
||||||
|
affected_items_dict = outliner_conform(apply=True, context=context)
|
||||||
message = []
|
message = []
|
||||||
for item in affected_items:
|
for _, v in affected_items_dict.items():
|
||||||
line = f'{item[0]} : {item[1]} {item[2]}'
|
for item, attr_list in v.items():
|
||||||
|
line = f'{item.name} : {", ".join(attr_list)}'
|
||||||
print(line)
|
print(line)
|
||||||
message.append(line)
|
message.append(line)
|
||||||
|
|
||||||
fn.show_message_box(
|
fn.show_message_box(
|
||||||
message=message,
|
message=message,
|
||||||
title=f"Conformed {len(affected_items)} in hierarchy",
|
title=f"Conformed {len(affected_items_dict)} in hierarchy",
|
||||||
icon='INFO'
|
icon='INFO'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
86
operators/store_visibility_states.py
Normal file
86
operators/store_visibility_states.py
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import bpy
|
||||||
|
from bpy.props import (BoolProperty,
|
||||||
|
EnumProperty,
|
||||||
|
PointerProperty,
|
||||||
|
CollectionProperty,
|
||||||
|
StringProperty)
|
||||||
|
from .. import fn
|
||||||
|
|
||||||
|
class RT_OT_store_visibility_states(bpy.types.Operator):
|
||||||
|
"""Store visibility states of objects and collections"""
|
||||||
|
bl_idname = "rt.store_visibility_states"
|
||||||
|
bl_label = "Store Visibility States"
|
||||||
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
|
collection_name: StringProperty(
|
||||||
|
name="Collection Name",
|
||||||
|
description="Name of the collection to store viewlayer states of a collection childrens. Leave empty for all.",
|
||||||
|
default=""
|
||||||
|
)
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
if self.collection_name == "ALL-SCENE-COLLECTION":
|
||||||
|
# Store scene collection (under key "ALL")
|
||||||
|
fn.store_collection_states(None, context=context)
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
# next((c for c in context.scene.collection.children_recursive if c.name == collection_name), None)
|
||||||
|
col = fn.get_target_collection(self.collection_name, context=context)
|
||||||
|
fn.store_collection_states(col, context=context)
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
class RT_OT_apply_visibility_states(bpy.types.Operator):
|
||||||
|
"""Apply stored visibility states to objects and collections"""
|
||||||
|
bl_idname = "rt.apply_visibility_states"
|
||||||
|
bl_label = "Apply Visibility States"
|
||||||
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
|
collection_name: StringProperty(
|
||||||
|
name="Collection Name",
|
||||||
|
description="Name of the collection to apply visibility states for. Leave empty for all.",
|
||||||
|
default=""
|
||||||
|
)
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
if self.collection_name == "ALL-SCENE-COLLECTION":
|
||||||
|
# Apply scene collection (under key "ALL")
|
||||||
|
ret = fn.apply_collection_states(None, context=context)
|
||||||
|
if isinstance(ret, tuple):
|
||||||
|
self.report(ret[0], ret[1])
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
# next((c for c in context.scene.collection.children_recursive if c.name == collection_name), None)
|
||||||
|
col = fn.get_target_collection(self.collection_name, context=context)
|
||||||
|
ret = fn.apply_collection_states(col, context=context)
|
||||||
|
if isinstance(ret, tuple):
|
||||||
|
self.report(ret[0], ret[1])
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
class RT_OT_delete_visibility_states(bpy.types.Operator):
|
||||||
|
"""Delete stored visibility states from scene"""
|
||||||
|
bl_idname = "rt.delete_visibility_states"
|
||||||
|
bl_label = "Delete Visibility States"
|
||||||
|
bl_options = {'REGISTER', 'UNDO'}
|
||||||
|
|
||||||
|
collection_name: StringProperty(
|
||||||
|
name="Collection Name",
|
||||||
|
description="Name of the collection to delete visibility states for. Leave empty for all.",
|
||||||
|
default=""
|
||||||
|
)
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
fn.delete_collection_states(self.collection_name, context=context)
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
def register():
|
||||||
|
bpy.utils.register_class(RT_OT_store_visibility_states)
|
||||||
|
bpy.utils.register_class(RT_OT_apply_visibility_states)
|
||||||
|
bpy.utils.register_class(RT_OT_delete_visibility_states)
|
||||||
|
|
||||||
|
|
||||||
|
def unregister():
|
||||||
|
bpy.utils.unregister_class(RT_OT_store_visibility_states)
|
||||||
|
bpy.utils.unregister_class(RT_OT_apply_visibility_states)
|
||||||
|
bpy.utils.unregister_class(RT_OT_delete_visibility_states)
|
117
properties.py
117
properties.py
@ -2,9 +2,14 @@ import bpy
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
from bpy.types import PropertyGroup
|
from bpy.types import PropertyGroup
|
||||||
from bpy.props import StringProperty
|
from bpy.props import (BoolProperty,
|
||||||
|
EnumProperty,
|
||||||
|
PointerProperty,
|
||||||
|
CollectionProperty,
|
||||||
|
StringProperty)
|
||||||
|
|
||||||
|
|
||||||
|
# region template props
|
||||||
class RT_PG_render_toolbox_props(PropertyGroup):
|
class RT_PG_render_toolbox_props(PropertyGroup):
|
||||||
default_base_path : StringProperty(
|
default_base_path : StringProperty(
|
||||||
name="Base Path Template",
|
name="Base Path Template",
|
||||||
@ -58,6 +63,110 @@ class RT_PG_render_toolbox_props(PropertyGroup):
|
|||||||
# # options={'SKIP_SAVE'}
|
# # options={'SKIP_SAVE'}
|
||||||
# )
|
# )
|
||||||
|
|
||||||
|
# endregion
|
||||||
|
|
||||||
|
# region visibility states
|
||||||
|
|
||||||
|
def name_search_callback(self, context, edit_text):
|
||||||
|
"""Search callback for collection names"""
|
||||||
|
## second arg is not displayed, can be and empty string...
|
||||||
|
if self.hierarchy_type == 'COLLECTION':
|
||||||
|
return [(c.name, '') for c in bpy.context.scene.collection.children_recursive if edit_text.lower() in c.name.lower()]
|
||||||
|
else:
|
||||||
|
return [(o.name, '') for o in bpy.context.scene.collection.all_objects if edit_text.lower() in o.name.lower()]
|
||||||
|
|
||||||
|
class RT_PG_render_toolbox_conform_props(PropertyGroup):
|
||||||
|
hierarchy_type: EnumProperty(
|
||||||
|
name="Hierarchy Type",
|
||||||
|
description="Choose whether to conform object hierarchy or collection hierarchy",
|
||||||
|
items=(
|
||||||
|
('COLLECTION', "Collection Hierarchy", "Conform collection hierarchy"),
|
||||||
|
('OBJECT', "Object Hierarchy", "Conform object hierarchy"),
|
||||||
|
),
|
||||||
|
default='COLLECTION'
|
||||||
|
)
|
||||||
|
|
||||||
|
## Utility prop : expose and control view layer hide state for active object
|
||||||
|
## just used for the update (actual bool value means nothing)
|
||||||
|
## can also use RT_PG_object_visibility from vis_conflict ops
|
||||||
|
## TODO: make an ops for this instead ?
|
||||||
|
# active_object_viewlayer_hide : BoolProperty(
|
||||||
|
# name="Active Object View Layer Hide",
|
||||||
|
# description="show / hide active object in current viewlayer",
|
||||||
|
# default=True,
|
||||||
|
# update=toggle_viewlayer_hide_state
|
||||||
|
# )
|
||||||
|
|
||||||
|
target_name: StringProperty(
|
||||||
|
name="Root Item",
|
||||||
|
description="Collection or object to target", # or object name # (useful for excluded collections)
|
||||||
|
default="",
|
||||||
|
search=name_search_callback
|
||||||
|
## basic collection fetch:
|
||||||
|
# search=lambda self, context, edit_text: [(c.name, '') for c in bpy.context.scene.collection.children_recursive if edit_text.lower() in c.name.lower()]
|
||||||
|
)
|
||||||
|
|
||||||
|
affect_target: EnumProperty(
|
||||||
|
name="Affect Target",
|
||||||
|
description="Choose whether to affect collections, objects, or both",
|
||||||
|
items=[
|
||||||
|
('COLLECTION', "Collection", "Affect collections only"),
|
||||||
|
('OBJECT', "Object", "Affect objects only"),
|
||||||
|
('ALL', "All", "Affect both collections and objects")
|
||||||
|
],
|
||||||
|
default='ALL'
|
||||||
|
)
|
||||||
|
|
||||||
|
## Common object and collection
|
||||||
|
|
||||||
|
conform_selectability: BoolProperty(
|
||||||
|
name="Hide Select State",
|
||||||
|
description="Conform hide select select",
|
||||||
|
default=False
|
||||||
|
)
|
||||||
|
|
||||||
|
conform_viewlayer: BoolProperty(
|
||||||
|
name="Hide in Viewlayer State",
|
||||||
|
description="Conform viewlayer temporary hide",
|
||||||
|
default=True
|
||||||
|
)
|
||||||
|
|
||||||
|
conform_viewport: BoolProperty(
|
||||||
|
name="Disable in Viewports State",
|
||||||
|
description="Conform the monitor icon (global viewport disable)",
|
||||||
|
default=False
|
||||||
|
)
|
||||||
|
|
||||||
|
conform_render: BoolProperty(
|
||||||
|
name="Disable in Renders State",
|
||||||
|
description="Conform the camera icon (render visibility)",
|
||||||
|
default=False
|
||||||
|
)
|
||||||
|
|
||||||
|
## Specific to collections
|
||||||
|
|
||||||
|
conform_exclude: BoolProperty(
|
||||||
|
name="Exclude View Layer State",
|
||||||
|
description="Conform the exclude from view layer",
|
||||||
|
default=True
|
||||||
|
)
|
||||||
|
|
||||||
|
conform_holdout: BoolProperty(
|
||||||
|
name="Holdout State",
|
||||||
|
description="Conform Collection Holdout State",
|
||||||
|
default=True
|
||||||
|
)
|
||||||
|
|
||||||
|
conform_use_indirect: BoolProperty(
|
||||||
|
name="Indirect Only State",
|
||||||
|
description="Conform Collection Indirect Only",
|
||||||
|
default=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# endregion
|
||||||
|
|
||||||
|
# region reset template functions
|
||||||
|
|
||||||
def reset_scene_path_templates():
|
def reset_scene_path_templates():
|
||||||
## set default template from environment variable if available, or fallback to preferences defaults
|
## set default template from environment variable if available, or fallback to preferences defaults
|
||||||
prefs = bpy.context.preferences.addons[__package__].preferences
|
prefs = bpy.context.preferences.addons[__package__].preferences
|
||||||
@ -80,9 +189,13 @@ def reset_scene_path_templates():
|
|||||||
else:
|
else:
|
||||||
bpy.context.scene.render_toolbox.default_multilayer_base_path = prefs.base_path_multilayer_template
|
bpy.context.scene.render_toolbox.default_multilayer_base_path = prefs.base_path_multilayer_template
|
||||||
|
|
||||||
|
# endregion
|
||||||
|
|
||||||
|
# region register
|
||||||
|
|
||||||
classes = (
|
classes = (
|
||||||
RT_PG_render_toolbox_props,
|
RT_PG_render_toolbox_props,
|
||||||
|
RT_PG_render_toolbox_conform_props,
|
||||||
)
|
)
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
@ -90,7 +203,7 @@ def register():
|
|||||||
bpy.utils.register_class(cls)
|
bpy.utils.register_class(cls)
|
||||||
|
|
||||||
bpy.types.Scene.render_toolbox = bpy.props.PointerProperty(type=RT_PG_render_toolbox_props)
|
bpy.types.Scene.render_toolbox = bpy.props.PointerProperty(type=RT_PG_render_toolbox_props)
|
||||||
# reset_scene_path_templates() # restricted context error when trying assignation here (probably need handler)
|
bpy.types.ViewLayer.render_toolbox_conform = bpy.props.PointerProperty(type=RT_PG_render_toolbox_conform_props) # not stored, no need to unregister
|
||||||
|
|
||||||
def unregister():
|
def unregister():
|
||||||
del bpy.types.Scene.render_toolbox
|
del bpy.types.Scene.render_toolbox
|
||||||
|
148
ui.py
148
ui.py
@ -1,6 +1,7 @@
|
|||||||
import bpy
|
import bpy
|
||||||
|
|
||||||
from bpy.types import Panel
|
from bpy.types import Panel
|
||||||
|
from . import fn
|
||||||
|
|
||||||
|
|
||||||
class RT_PT_render_toolbox_ui(Panel):
|
class RT_PT_render_toolbox_ui(Panel):
|
||||||
@ -59,8 +60,150 @@ class RT_PT_conformation_ui(bpy.types.Panel):
|
|||||||
|
|
||||||
def draw(self, context):
|
def draw(self, context):
|
||||||
layout = self.layout
|
layout = self.layout
|
||||||
layout.operator("rt.conform_collection_hierarchy",
|
|
||||||
text="Conform Collection Hierarchy", icon="OUTLINER_COLLECTION")
|
## Show properties for outliner conformation
|
||||||
|
layout.use_property_split = True
|
||||||
|
layout.use_property_decorate = False
|
||||||
|
|
||||||
|
# props = context.scene.render_toolbox_conform
|
||||||
|
props = context.view_layer.render_toolbox_conform
|
||||||
|
|
||||||
|
col = layout.column(align=True)
|
||||||
|
col.prop(props, "hierarchy_type", text="Work On") # , expand=True
|
||||||
|
# col.separator()
|
||||||
|
col.prop(props, "target_name", text="Search") # (Optional)
|
||||||
|
|
||||||
|
## Show current target
|
||||||
|
box = layout.box()
|
||||||
|
tgt_row = box.row()
|
||||||
|
|
||||||
|
if props.hierarchy_type == 'COLLECTION':
|
||||||
|
ref_collection = fn.get_target_collection(props.target_name, context)
|
||||||
|
if not ref_collection or ref_collection == context.scene.collection:
|
||||||
|
layout.label(text="Select a collection or search by name", icon='INFO')
|
||||||
|
if ref_collection == context.scene.collection:
|
||||||
|
layout.label(text="Cannot use the scene collection", icon='ERROR')
|
||||||
|
layout.label(text="An excluded collection collection cannot be active (use search)")
|
||||||
|
return
|
||||||
|
|
||||||
|
if not ref_collection:
|
||||||
|
layout.label(text=f"Error: Collection '{ref_collection.name}' not found", icon='ERROR')
|
||||||
|
return
|
||||||
|
|
||||||
|
ref_vlc = fn.get_view_layer_collection(ref_collection)
|
||||||
|
if not ref_vlc:
|
||||||
|
layout.label(text=f"Error: Viewlayer Collection '{ref_collection.name}' not found", icon='ERROR')
|
||||||
|
return
|
||||||
|
|
||||||
|
# tgt_row = layout.row(align=True)
|
||||||
|
# tgt_row.label(text="", icon='TRIA_RIGHT')
|
||||||
|
tgt_row.label(text=ref_collection.name, icon='OUTLINER_COLLECTION')
|
||||||
|
col.operator("rt.store_visibility_states", text='Store target State', icon="DISK_DRIVE")
|
||||||
|
|
||||||
|
## Show current collection state (behave badly when changed, should be tweaked before)
|
||||||
|
# col = layout.column(align=True)
|
||||||
|
row = tgt_row.row(align=True)
|
||||||
|
row.prop(ref_vlc, "exclude", text="", emboss=False)
|
||||||
|
row.prop(ref_collection, "hide_select", text="", emboss=False)
|
||||||
|
row.prop(ref_vlc, "hide_viewport", text="", emboss=False)
|
||||||
|
row.prop(ref_collection, "hide_viewport", text="", emboss=False)
|
||||||
|
row.prop(ref_collection, "hide_render", text="", emboss=False)
|
||||||
|
row.prop(ref_vlc, "holdout", text="", emboss=False)
|
||||||
|
row.prop(ref_vlc, "indirect_only", text="", emboss=False)
|
||||||
|
|
||||||
|
layout.prop(props, "affect_target", text="Target Items")
|
||||||
|
layout.separator()
|
||||||
|
|
||||||
|
col = layout.column(align=True)
|
||||||
|
row = col.row(align=True)
|
||||||
|
row.label(text="Parameter To Conform:")
|
||||||
|
## Same order, greyout unused options
|
||||||
|
collec_row = row.row(align=True)
|
||||||
|
collec_row.prop(props, "conform_exclude", text="", icon='CHECKBOX_DEHLT' if ref_vlc.exclude else 'CHECKBOX_HLT') # Exclude from View Layer
|
||||||
|
collec_row.active = props.affect_target != 'OBJECT'
|
||||||
|
|
||||||
|
## Object and collections
|
||||||
|
row.prop(props, "conform_selectability", text="", icon='RESTRICT_SELECT_ON' if ref_collection.hide_select else 'RESTRICT_SELECT_OFF') # Hide Select
|
||||||
|
row.prop(props, "conform_viewlayer", text="", icon='HIDE_ON' if ref_vlc.hide_viewport else 'HIDE_OFF') # Hide in current viewlayer (eye)
|
||||||
|
row.prop(props, "conform_viewport", text="", icon='RESTRICT_VIEW_ON' if ref_collection.hide_viewport else 'RESTRICT_VIEW_OFF') # Disable in Viewports
|
||||||
|
row.prop(props, "conform_render", text="", icon='RESTRICT_RENDER_ON' if ref_collection.hide_render else 'RESTRICT_RENDER_OFF') # Disable in Renders
|
||||||
|
|
||||||
|
## Specific to collections
|
||||||
|
collec_row = row.row(align=True)
|
||||||
|
collec_row.prop(props, "conform_holdout", text="", icon='HOLDOUT_OFF') # Holdout
|
||||||
|
collec_row.prop(props, "conform_use_indirect", text="", icon='INDIRECT_ONLY_OFF') # Indirect Only
|
||||||
|
collec_row.active = props.affect_target != 'OBJECT'
|
||||||
|
|
||||||
|
else:
|
||||||
|
ref_obj = fn.get_target_object(props.target_name, context)
|
||||||
|
|
||||||
|
if not ref_obj:
|
||||||
|
layout.label(text="Make object active or search by name", icon='INFO')
|
||||||
|
return
|
||||||
|
|
||||||
|
# tgt_row = layout.row(align=True)
|
||||||
|
# tgt_row.label(text="", icon='TRIA_RIGHT')
|
||||||
|
tgt_row.label(text=ref_obj.name, icon='OBJECT_DATA')
|
||||||
|
|
||||||
|
if not ref_obj.children_recursive:
|
||||||
|
tgt_row.label(text="Object has no children", icon='ERROR')
|
||||||
|
return
|
||||||
|
|
||||||
|
layout.separator()
|
||||||
|
## Show current collection state (can behave badly when changed, should be tweaked before)
|
||||||
|
# col = layout.column(align=True)
|
||||||
|
row = tgt_row.row(align=True)
|
||||||
|
# row.label(text="Reference Object State:")
|
||||||
|
row.prop(ref_obj, "hide_select", text="", emboss=False)
|
||||||
|
# row.prop(props, "active_object_viewlayer_hide", text="", icon='HIDE_ON' if ref_obj.hide_get() else 'HIDE_OFF', emboss=False) # hack
|
||||||
|
row.label(text="", icon='HIDE_ON' if ref_obj.hide_get() else 'HIDE_OFF') # hack
|
||||||
|
row.prop(ref_obj, "hide_viewport", text="", emboss=False)
|
||||||
|
row.prop(ref_obj, "hide_render", text="", emboss=False)
|
||||||
|
|
||||||
|
layout.separator()
|
||||||
|
col = layout.column(align=True)
|
||||||
|
row = col.row(align=True)
|
||||||
|
row.label(text="Parameter To Conform:")
|
||||||
|
row.prop(props, "conform_selectability", text="", icon='RESTRICT_SELECT_ON' if ref_obj.hide_select else 'RESTRICT_SELECT_OFF') # Hide Select
|
||||||
|
row.prop(props, "conform_viewlayer", text="", icon='HIDE_ON' if ref_obj.hide_get() else 'HIDE_OFF') # Hide in current viewlayer (eye)
|
||||||
|
row.prop(props, "conform_viewport", text="", icon='RESTRICT_VIEW_ON' if ref_obj.hide_viewport else 'RESTRICT_VIEW_OFF') # Disable in Viewports
|
||||||
|
row.prop(props, "conform_render", text="", icon='RESTRICT_RENDER_ON' if ref_obj.hide_render else 'RESTRICT_RENDER_OFF') # Disable in Renders
|
||||||
|
|
||||||
|
|
||||||
|
layout.operator("rt.conform_collection_hierarchy",text="Conform Hierarchy", icon="CHECKMARK")
|
||||||
|
|
||||||
|
|
||||||
|
class RT_PT_outliner_state_ui(bpy.types.Panel):
|
||||||
|
bl_space_type = "VIEW_3D"
|
||||||
|
bl_region_type = "UI"
|
||||||
|
bl_category = "View"
|
||||||
|
bl_label = "Outliner State"
|
||||||
|
bl_options = {'DEFAULT_CLOSED'}
|
||||||
|
bl_parent_id = "RT_PT_conformation_ui"
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
## Outliner state
|
||||||
|
|
||||||
|
## Scene
|
||||||
|
col = layout.column()
|
||||||
|
col.operator("rt.store_visibility_states", text='Store Whole Viewlayer State', icon="DISK_DRIVE").collection_name = "ALL-SCENE-COLLECTION"
|
||||||
|
stored_keys = context.scene.get('outliner_state', {}).keys()
|
||||||
|
if stored_keys and 'ALL' in stored_keys:
|
||||||
|
row = col.row(align=True)
|
||||||
|
row.operator("rt.apply_visibility_states", text=f"Restore Viewlayer State", icon="OUTLINER").collection_name = "ALL-SCENE-COLLECTION"
|
||||||
|
row.operator("rt.delete_visibility_states", text="", icon="TRASH").collection_name = 'ALL'
|
||||||
|
|
||||||
|
col = layout.column()
|
||||||
|
## Specific collection
|
||||||
|
if stored_keys:
|
||||||
|
col.label(text="Collections State:")
|
||||||
|
for key in stored_keys:
|
||||||
|
if key == 'ALL':
|
||||||
|
continue
|
||||||
|
row = col.row(align=True)
|
||||||
|
row.operator("rt.apply_visibility_states", text=f"Restore: {key}", icon="OUTLINER").collection_name = key
|
||||||
|
row.operator("rt.delete_visibility_states", text="", icon="TRASH").collection_name = key
|
||||||
|
|
||||||
## Unused, only exposed in Create output panel
|
## Unused, only exposed in Create output panel
|
||||||
# class RT_PT_output_template(Panel):
|
# class RT_PT_output_template(Panel):
|
||||||
@ -94,6 +237,7 @@ classes = (
|
|||||||
RT_PT_visibility_check_ui_viewport,
|
RT_PT_visibility_check_ui_viewport,
|
||||||
RT_PT_visibility_check_ui_node,
|
RT_PT_visibility_check_ui_node,
|
||||||
RT_PT_conformation_ui,
|
RT_PT_conformation_ui,
|
||||||
|
RT_PT_outliner_state_ui,
|
||||||
# RT_PT_output_template,
|
# RT_PT_output_template,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user