Newer
Older
if not view_layer in internals.rto_history["holdout"]:
internals.rto_history["holdout"][view_layer] = {"target": "", "history": []}
if modifiers == {"alt"}:
del internals.rto_history["holdout"][view_layer]
cls.isolated = False
elif modifiers == {"shift"}:
isolate_rto(cls, self, view_layer, "holdout")
elif modifiers == {"ctrl"}:
toggle_children(self, view_layer, "holdout")
cls.isolated = False
elif modifiers == {"ctrl", "shift"}:
isolate_rto(cls, self, view_layer, "holdout", children=True)
else:
# toggle holdout
# reset holdout history
del internals.rto_history["holdout"][view_layer]
# toggle holdout of collection in viewport
laycol_ptr.holdout = not laycol_ptr.holdout
cls.isolated = False
# reset holdout all history
if view_layer in internals.rto_history["holdout_all"]:
del internals.rto_history["holdout_all"][view_layer]
return {'FINISHED'}
class CMUnHoldoutAllOperator(Operator):
bl_label = "[HH Global] Holdout"
bl_description = (
" * LMB - Enable all/Restore.\n"
" * Shift+LMB - Invert.\n"
" * Shift+Ctrl+LMB - Isolate collections w/ selected objects.\n"
" * Shift+Alt+LMB - Disable collections w/ selected objects.\n"
" * Ctrl+LMB - Copy/Paste RTOs.\n"
" * Ctrl+Alt+LMB - Swap RTOs.\n"
" * Alt+LMB - Discard history"
)
bl_idname = "view3d.un_holdout_all_collections"
bl_options = {'REGISTER', 'UNDO'}
def invoke(self, context, event):
view_layer = context.view_layer.name
modifiers = get_modifiers(event)
if not view_layer in internals.rto_history["holdout_all"]:
internals.rto_history["holdout_all"][view_layer] = []
if modifiers == {"alt"}:
# clear all states
del internals.rto_history["holdout_all"][view_layer]
clear_copy("holdout")
clear_swap("holdout")
elif modifiers == {"ctrl"}:
copy_rtos(view_layer, "holdout")
elif modifiers == {"ctrl", "alt"}:
swap_rtos(view_layer, "holdout")
elif modifiers == {"shift"}:
invert_rtos(view_layer, "holdout")
elif modifiers == {"shift", "ctrl"}:
error = isolate_sel_objs_collections(view_layer, "holdout", "CM")
if error:
self.report({"WARNING"}, error)
return {'CANCELLED'}
elif modifiers == {"shift", "alt"}:
error = disable_sel_objs_collections(view_layer, "holdout", "CM")
if error:
self.report({"WARNING"}, error)
return {'CANCELLED'}
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
else:
activate_all_rtos(view_layer, "holdout")
return {'FINISHED'}
class CMIndirectOnlyOperator(Operator):
bl_label = "[IO] Indirect Only"
bl_description = (
" * Shift+LMB - Isolate/Restore.\n"
" * Shift+Ctrl+LMB - Isolate nested/Restore.\n"
" * Ctrl+LMB - Toggle nested.\n"
" * Alt+LMB - Discard history"
)
bl_idname = "view3d.indirect_only_collection"
bl_options = {'REGISTER', 'UNDO'}
name: StringProperty()
# static class var
isolated = False
def invoke(self, context, event):
cls = CMIndirectOnlyOperator
modifiers = get_modifiers(event)
view_layer = context.view_layer.name
laycol_ptr = internals.layer_collections[self.name]["ptr"]
if not view_layer in internals.rto_history["indirect"]:
internals.rto_history["indirect"][view_layer] = {"target": "", "history": []}
if modifiers == {"alt"}:
del internals.rto_history["indirect"][view_layer]
cls.isolated = False
elif modifiers == {"shift"}:
isolate_rto(cls, self, view_layer, "indirect")
elif modifiers == {"ctrl"}:
toggle_children(self, view_layer, "indirect")
cls.isolated = False
elif modifiers == {"ctrl", "shift"}:
isolate_rto(cls, self, view_layer, "indirect", children=True)
else:
# toggle indirect only
# reset indirect history
del internals.rto_history["indirect"][view_layer]
# toggle indirect only of collection
laycol_ptr.indirect_only = not laycol_ptr.indirect_only
cls.isolated = False
# reset indirect all history
if view_layer in internals.rto_history["indirect_all"]:
del internals.rto_history["indirect_all"][view_layer]
return {'FINISHED'}
class CMUnIndirectOnlyAllOperator(Operator):
bl_label = "[IO Global] Indirect Only"
bl_description = (
" * LMB - Enable all/Restore.\n"
" * Shift+LMB - Invert.\n"
" * Shift+Ctrl+LMB - Isolate collections w/ selected objects.\n"
" * Shift+Alt+LMB - Disable collections w/ selected objects.\n"
" * Ctrl+LMB - Copy/Paste RTOs.\n"
" * Ctrl+Alt+LMB - Swap RTOs.\n"
" * Alt+LMB - Discard history"
)
bl_idname = "view3d.un_indirect_only_all_collections"
bl_options = {'REGISTER', 'UNDO'}
def invoke(self, context, event):
view_layer = context.view_layer.name
modifiers = get_modifiers(event)
if not view_layer in internals.rto_history["indirect_all"]:
internals.rto_history["indirect_all"][view_layer] = []
if modifiers == {"alt"}:
# clear all states
del internals.rto_history["indirect_all"][view_layer]
clear_copy("indirect")
clear_swap("indirect")
elif modifiers == {"ctrl"}:
copy_rtos(view_layer, "indirect")
elif modifiers == {"ctrl", "alt"}:
swap_rtos(view_layer, "indirect")
elif modifiers == {"shift"}:
invert_rtos(view_layer, "indirect")
elif modifiers == {"shift", "ctrl"}:
error = isolate_sel_objs_collections(view_layer, "indirect", "CM")
if error:
self.report({"WARNING"}, error)
return {'CANCELLED'}
elif modifiers == {"shift", "alt"}:
error = disable_sel_objs_collections(view_layer, "indirect", "CM")
if error:
self.report({"WARNING"}, error)
return {'CANCELLED'}
else:
activate_all_rtos(view_layer, "indirect")
return {'FINISHED'}
class CMRemoveCollectionOperator(Operator):
'''Remove Collection'''
bl_label = "Remove Collection"
bl_idname = "view3d.remove_collection"
bl_options = {'UNDO'}
collection_name: StringProperty()
def execute(self, context):
laycol = internals.layer_collections[self.collection_name]
collection = laycol["ptr"].collection
parent_collection = laycol["parent"]["ptr"].collection
# shift all objects in this collection to the parent collection
for obj in collection.objects:
if obj.name not in parent_collection.objects:
parent_collection.objects.link(obj)
# shift all child collections to the parent collection preserving view layer RTOs
link_child_collections_to_parent(laycol, collection, parent_collection)
# remove collection, update references, and update tree view
remove_collection(laycol, collection, context)
class CMRemoveEmptyCollectionsOperator(Operator):
bl_label = "Remove Empty Collections"
bl_idname = "view3d.remove_empty_collections"
bl_options = {'UNDO'}
without_objects: BoolProperty()
@classmethod
def description(cls, context, properties):
if properties.without_objects:
tooltip = (
"Purge All Collections Without Objects.\n"
"Deletes all collections that don't contain objects even if they have subcollections"
)
else:
tooltip = (
"Remove Empty Collections.\n"
"Delete collections that don't have any subcollections or objects"
)
def execute(self, context):
if self.without_objects:
empty_collections = [laycol["name"]
for laycol in internals.layer_collections.values()
if not laycol["ptr"].collection.objects]
else:
empty_collections = [laycol["name"]
for laycol in internals.layer_collections.values()
if not laycol["children"] and
not laycol["ptr"].collection.objects]
for name in empty_collections:
laycol = internals.layer_collections[name]
collection = laycol["ptr"].collection
parent_collection = laycol["parent"]["ptr"].collection
# link all child collections to the parent collection preserving view layer RTOs
if collection.children:
link_child_collections_to_parent(laycol, collection, parent_collection)
# remove collection, update references, and update tree view
remove_collection(laycol, collection, context)
self.report({"INFO"}, f"Removed {len(empty_collections)} collections")
class CMNewCollectionOperator(Operator):
bl_label = "Add New Collection"
bl_idname = "view3d.add_collection"
bl_options = {'UNDO'}
@classmethod
def description(cls, context, properties):
if properties.child:
tooltip = (
"Add New SubCollection.\n"
"Add a new subcollection to the currently selected collection"
)
else:
tooltip = (
"Add New Collection.\n"
"Add a new collection as a sibling of the currently selected collection"
)
return tooltip
def execute(self, context):
new_collection = bpy.data.collections.new("New Collection")
cm = context.scene.collection_manager
# prevent adding collections when collections are filtered
# and the selection is ambiguous
if cm.cm_list_index == -1 and ui.CM_UL_items.filtering:
send_report("Cannot create new collection.\n"
"No collection is selected and collections are filtered."
)
return {'CANCELLED'}
if cm.cm_list_index > -1 and not ui.CM_UL_items.visible_items[cm.cm_list_index]:
send_report("Cannot create new collection.\n"
"The selected collection isn't visible."
)
return {'CANCELLED'}
# if there are collections
if len(cm.cm_list_collection) > 0:
if not cm.cm_list_index == -1:
# get selected collection
laycol = internals.layer_collections[cm.cm_list_collection[cm.cm_list_index].name]
# add new collection
if self.child:
laycol["ptr"].collection.children.link(new_collection)
internals.expanded.add(laycol["name"])
# update tree view property
update_property_group(context)
cm.cm_list_index = internals.layer_collections[new_collection.name]["row_index"]
else:
laycol["parent"]["ptr"].collection.children.link(new_collection)
# update tree view property
update_property_group(context)
cm.cm_list_index = internals.layer_collections[new_collection.name]["row_index"]
context.scene.collection.children.link(new_collection)
# update tree view property
update_property_group(context)
cm.cm_list_index = internals.layer_collections[new_collection.name]["row_index"]
# if no collections add top level collection and select it
else:
context.scene.collection.children.link(new_collection)
# update tree view property
update_property_group(context)
# set new collection to active
layer_collection = internals.layer_collections[new_collection.name]["ptr"]
context.view_layer.active_layer_collection = layer_collection
# show the new collection when collections are filtered.
ui.CM_UL_items.new_collections.append(new_collection.name)
global rename
rename[0] = True
for rto in internals.rto_history.values():
class CMPhantomModeOperator(Operator):
bl_label = "Toggle Phantom Mode"
bl_idname = "view3d.toggle_phantom_mode"
bl_description = (
"Phantom Mode\n"
"Saves the state of all RTOs and only allows changes to them, on exit all RTOs are returned to their saved state.\n"
"Note: modifying collections (except RTOs) externally will exit Phantom Mode and your initial state will be lost"
)
def execute(self, context):
cm = context.scene.collection_manager
view_layer = context.view_layer
# save current visibility state
internals.phantom_history["view_layer"] = view_layer.name
def save_visibility_state(layer_collection):
internals.phantom_history["initial_state"][layer_collection.name] = {
"exclude": layer_collection.exclude,
"select": layer_collection.collection.hide_select,
"hide": layer_collection.hide_viewport,
"disable": layer_collection.collection.hide_viewport,
"render": layer_collection.collection.hide_render,
"holdout": layer_collection.holdout,
"indirect": layer_collection.indirect_only,
apply_to_children(view_layer.layer_collection, save_visibility_state)
# save current rto history
for rto, history, in internals.rto_history.items():
if history.get(view_layer.name, None):
internals.phantom_history[rto+"_history"] = deepcopy(history[view_layer.name])
else: # return to normal mode
def restore_visibility_state(layer_collection):
phantom_laycol = internals.phantom_history["initial_state"][layer_collection.name]
layer_collection.exclude = phantom_laycol["exclude"]
layer_collection.collection.hide_select = phantom_laycol["select"]
layer_collection.hide_viewport = phantom_laycol["hide"]
layer_collection.collection.hide_viewport = phantom_laycol["disable"]
layer_collection.collection.hide_render = phantom_laycol["render"]
layer_collection.holdout = phantom_laycol["holdout"]
layer_collection.indirect_only = phantom_laycol["indirect"]
apply_to_children(view_layer.layer_collection, restore_visibility_state)
# restore previous rto history
for rto, history, in internals.rto_history.items():
if view_layer.name in history:
del history[view_layer.name]
if internals.phantom_history[rto+"_history"]:
history[view_layer.name] = deepcopy(internals.phantom_history[rto+"_history"])
internals.phantom_history[rto+"_history"].clear()
class CMApplyPhantomModeOperator(Operator):
'''Apply changes and quit Phantom Mode'''
bl_label = "Apply Phantom Mode"
bl_idname = "view3d.apply_phantom_mode"
def execute(self, context):
cm = context.scene.collection_manager
cm.in_phantom_mode = False
return {'FINISHED'}
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
class CMDisableObjectsOperator(Operator):
'''Disable selected objects in viewports'''
bl_label = "Disable Selected"
bl_idname = "view3d.disable_selected_objects"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
for obj in context.selected_objects:
obj.hide_viewport = True
return {'FINISHED'}
class CMDisableUnSelectedObjectsOperator(Operator):
'''Disable unselected objects in viewports'''
bl_label = "Disable Unselected"
bl_idname = "view3d.disable_unselected_objects"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
for obj in bpy.data.objects:
if obj in context.visible_objects and not obj in context.selected_objects:
obj.hide_viewport = True
return {'FINISHED'}
class CMRestoreDisabledObjectsOperator(Operator):
'''Restore disabled objects in viewports'''
bl_label = "Restore Disabled Objects"
bl_idname = "view3d.restore_disabled_objects"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
for obj in bpy.data.objects:
if obj.hide_viewport:
obj.hide_viewport = False
obj.select_set(True)
return {'FINISHED'}
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
class CMUndoWrapper(Operator):
bl_label = "Undo"
bl_description = "Undo previous action"
bl_idname = "view3d.undo_wrapper"
@classmethod
def poll(self, context):
return bpy.ops.ed.undo.poll()
def execute(self, context):
internals.collection_state.clear()
internals.collection_state.update(generate_state())
bpy.ops.ed.undo()
update_property_group(context)
check_state(context, cm_popup=True)
# clear buffers
internals.copy_buffer["RTO"] = ""
internals.copy_buffer["values"].clear()
internals.swap_buffer["A"]["RTO"] = ""
internals.swap_buffer["A"]["values"].clear()
internals.swap_buffer["B"]["RTO"] = ""
internals.swap_buffer["B"]["values"].clear()
return {'FINISHED'}
class CMRedoWrapper(Operator):
bl_label = "Redo"
bl_description = "Redo previous action"
bl_idname = "view3d.redo_wrapper"
@classmethod
def poll(self, context):
return bpy.ops.ed.redo.poll()
def execute(self, context):
bpy.ops.ed.redo()
update_property_group(context)
return {'FINISHED'}