Newer
Older
# SPDX-License-Identifier: GPL-2.0-or-later
# For VARS
from . import internals
# For FUNCTIONS
get_move_active,
mode_converter = {
'EDIT_MESH': 'EDIT',
'EDIT_CURVE': 'EDIT',
'EDIT_SURFACE': 'EDIT',
'EDIT_TEXT': 'EDIT',
'EDIT_ARMATURE': 'EDIT',
'EDIT_METABALL': 'EDIT',
'EDIT_LATTICE': 'EDIT',
'POSE': 'POSE',
'SCULPT': 'SCULPT',
'PAINT_WEIGHT': 'WEIGHT_PAINT',
'PAINT_VERTEX': 'VERTEX_PAINT',
'PAINT_TEXTURE': 'TEXTURE_PAINT',
'PARTICLE': 'PARTICLE_EDIT',
'OBJECT': 'OBJECT',
'PAINT_GPENCIL': 'PAINT_GPENCIL',
'EDIT_GPENCIL': 'EDIT_GPENCIL',
'SCULPT_GPENCIL': 'SCULPT_GPENCIL',
'WEIGHT_GPENCIL': 'WEIGHT_GPENCIL',
'VERTEX_GPENCIL': 'VERTEX_GPENCIL',
}
rto_path = {
"exclude": "exclude",
"select": "collection.hide_select",
"hide": "hide_viewport",
"disable": "collection.hide_viewport",
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
"render": "collection.hide_render",
"holdout": "holdout",
"indirect": "indirect_only",
}
set_off_on = {
"exclude": {
"off": True,
"on": False
},
"select": {
"off": True,
"on": False
},
"hide": {
"off": True,
"on": False
},
"disable": {
"off": True,
"on": False
},
"render": {
"off": True,
"on": False
},
"holdout": {
"off": False,
"on": True
},
"indirect": {
"off": False,
"on": True
}
}
get_off_on = {
False: {
"exclude": "on",
"select": "on",
"hide": "on",
"disable": "on",
"render": "on",
"holdout": "off",
"indirect": "off",
},
True: {
"exclude": "off",
"select": "off",
"hide": "off",
"disable": "off",
"render": "off",
"holdout": "on",
"indirect": "on",
}
}
def get_rto(layer_collection, rto):
if rto in ["exclude", "hide", "holdout", "indirect"]:
return getattr(layer_collection, rto_path[rto])
else:
collection = getattr(layer_collection, "collection")
return getattr(collection, rto_path[rto].split(".")[1])
def set_rto(layer_collection, rto, value):
if rto in ["exclude", "hide", "holdout", "indirect"]:
setattr(layer_collection, rto_path[rto], value)
else:
collection = getattr(layer_collection, "collection")
setattr(collection, rto_path[rto].split(".")[1], value)
def apply_to_children(parent, apply_function, *args, **kwargs):
# works for both Collections & LayerCollections
child_lists = [parent.children]
while child_lists:
new_child_lists = []
for child_list in child_lists:
for child in child_list:
apply_function(child, *args, **kwargs)
if child.children:
new_child_lists.append(child.children)
child_lists = new_child_lists
def isolate_rto(cls, self, view_layer, rto, *, children=False):
off = set_off_on[rto]["off"]
on = set_off_on[rto]["on"]
laycol_ptr = internals.layer_collections[self.name]["ptr"]
target = internals.rto_history[rto][view_layer]["target"]
history = internals.rto_history[rto][view_layer]["history"]
active_layer_collections = [x["ptr"] for x in internals.layer_collections.values()
if get_rto(x["ptr"], rto) == on]
# check if previous state should be restored
if cls.isolated and self.name == target:
# restore previous state
for x, item in enumerate(internals.layer_collections.values()):
set_rto(item["ptr"], rto, history[x])
# reset target and history
del internals.rto_history[rto][view_layer]
cls.isolated = False
# check if all RTOs should be activated
elif (len(active_layer_collections) == 1 and
active_layer_collections[0].name == self.name):
# activate all collections
for item in internals.layer_collections.values():
set_rto(item["ptr"], rto, on)
del internals.rto_history[rto][view_layer]
cls.isolated = False
else:
# isolate collection
internals.rto_history[rto][view_layer]["target"] = self.name
# reset history
history.clear()
# save state
for item in internals.layer_collections.values():
history.append(get_rto(item["ptr"], rto))
child_states = {}
if children:
# get child states
def get_child_states(layer_collection):
child_states[layer_collection.name] = get_rto(layer_collection, rto)
apply_to_children(laycol_ptr, get_child_states)
# isolate collection
for item in internals.layer_collections.values():
if item["name"] != laycol_ptr.name:
set_rto(item["ptr"], rto, off)
set_rto(laycol_ptr, rto, on)
if rto not in ["exclude", "holdout", "indirect"]:
laycol = internals.layer_collections[self.name]
set_rto(laycol["ptr"], rto, on)
laycol = laycol["parent"]
if children:
# restore child states
def restore_child_states(layer_collection):
set_rto(layer_collection, rto, child_states[layer_collection.name])
apply_to_children(laycol_ptr, restore_child_states)
else:
if children:
# restore child states
def restore_child_states(layer_collection):
set_rto(layer_collection, rto, child_states[layer_collection.name])
apply_to_children(laycol_ptr, restore_child_states)
elif rto == "exclude":
# deactivate all children
def deactivate_all_children(layer_collection):
set_rto(layer_collection, rto, True)
apply_to_children(laycol_ptr, deactivate_all_children)
cls.isolated = True
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
def isolate_sel_objs_collections(view_layer, rto, caller, *, use_active=False):
selected_objects = get_move_selection()
if use_active:
selected_objects.add(get_move_active(always=True))
if not selected_objects:
return "No selected objects"
off = set_off_on[rto]["off"]
on = set_off_on[rto]["on"]
if caller == "CM":
history = internals.rto_history[rto+"_all"][view_layer]
elif caller == "QCD":
history = internals.qcd_history[view_layer]
# if not isolated, isolate collections of selected objects
if len(history) == 0:
keep_history = False
# save history and isolate RTOs
for item in internals.layer_collections.values():
history.append(get_rto(item["ptr"], rto))
rto_state = off
# check if any of the selected objects are in the collection
if not set(selected_objects).isdisjoint(item["ptr"].collection.objects):
rto_state = on
if history[-1] != rto_state:
keep_history = True
if rto == "exclude":
set_exclude_state(item["ptr"], rto_state)
else:
set_rto(item["ptr"], rto, rto_state)
# activate all parents if needed
if rto_state == on and rto not in ["holdout", "indirect"]:
laycol = item["parent"]
while laycol["id"] != 0:
set_rto(laycol["ptr"], rto, on)
laycol = laycol["parent"]
if not keep_history:
history.clear()
return "Collection already isolated"
else:
for x, item in enumerate(internals.layer_collections.values()):
set_rto(item["ptr"], rto, history[x])
# clear history
if caller == "CM":
del internals.rto_history[rto+"_all"][view_layer]
elif caller == "QCD":
del internals.qcd_history[view_layer]
def disable_sel_objs_collections(view_layer, rto, caller):
off = set_off_on[rto]["off"]
on = set_off_on[rto]["on"]
selected_objects = get_move_selection()
if caller == "CM":
history = internals.rto_history[rto+"_all"][view_layer]
elif caller == "QCD":
history = internals.qcd_history[view_layer]
if not selected_objects and not history:
# clear history
if caller == "CM":
del internals.rto_history[rto+"_all"][view_layer]
elif caller == "QCD":
del internals.qcd_history[view_layer]
return "No selected objects"
# if not disabled, disable collections of selected objects
if len(history) == 0:
# save history and disable RTOs
for item in internals.layer_collections.values():
history.append(get_rto(item["ptr"], rto))
# check if any of the selected objects are in the collection
if not set(selected_objects).isdisjoint(item["ptr"].collection.objects):
if rto == "exclude":
set_exclude_state(item["ptr"], off)
else:
set_rto(item["ptr"], rto, off)
else:
for x, item in enumerate(internals.layer_collections.values()):
set_rto(item["ptr"], rto, history[x])
# clear history
if caller == "CM":
del internals.rto_history[rto+"_all"][view_layer]
elif caller == "QCD":
del internals.qcd_history[view_layer]
def toggle_children(self, view_layer, rto):
laycol_ptr = internals.layer_collections[self.name]["ptr"]
# clear rto history
del internals.rto_history[rto][view_layer]
internals.rto_history[rto+"_all"].pop(view_layer, None)
# toggle rto state
state = not get_rto(laycol_ptr, rto)
set_rto(laycol_ptr, rto, state)
def set_state(layer_collection):
set_rto(layer_collection, rto, state)
apply_to_children(laycol_ptr, set_state)
def activate_all_rtos(view_layer, rto):
off = set_off_on[rto]["off"]
on = set_off_on[rto]["on"]
history = internals.rto_history[rto+"_all"][view_layer]
# if not activated, activate all
if len(history) == 0:
keep_history = False
for item in reversed(list(internals.layer_collections.values())):
if get_rto(item["ptr"], rto) == off:
keep_history = True
history.append(get_rto(item["ptr"], rto))
set_rto(item["ptr"], rto, on)
if not keep_history:
history.clear()
history.reverse()
else:
for x, item in enumerate(internals.layer_collections.values()):
set_rto(item["ptr"], rto, history[x])
# clear rto history
del internals.rto_history[rto+"_all"][view_layer]
def invert_rtos(view_layer, rto):
if rto == "exclude":
orig_values = []
for item in internals.layer_collections.values():
orig_values.append(get_rto(item["ptr"], rto))
for x, item in enumerate(internals.layer_collections.values()):
set_rto(item["ptr"], rto, not orig_values[x])
else:
for item in internals.layer_collections.values():
set_rto(item["ptr"], rto, not get_rto(item["ptr"], rto))
# clear rto history
internals.rto_history[rto].pop(view_layer, None)
def copy_rtos(view_layer, rto):
if not internals.copy_buffer["RTO"]:
internals.copy_buffer["RTO"] = rto
for laycol in internals.layer_collections.values():
internals.copy_buffer["values"].append(get_off_on[
get_rto(laycol["ptr"], rto)
][
rto
]
)
for x, laycol in enumerate(internals.layer_collections.values()):
set_rto(laycol["ptr"],
rto,
set_off_on[rto][
internals.copy_buffer["values"][x]
# clear rto history
internals.rto_history[rto].pop(view_layer, None)
del internals.rto_history[rto+"_all"][view_layer]
internals.copy_buffer["RTO"] = ""
internals.copy_buffer["values"].clear()
def swap_rtos(view_layer, rto):
if not internals.swap_buffer["A"]["values"]:
internals.swap_buffer["A"]["RTO"] = rto
for laycol in internals.layer_collections.values():
internals.swap_buffer["A"]["values"].append(get_off_on[
get_rto(laycol["ptr"], rto)
][
rto
]
)
internals.swap_buffer["B"]["RTO"] = rto
for laycol in internals.layer_collections.values():
internals.swap_buffer["B"]["values"].append(get_off_on[
get_rto(laycol["ptr"], rto)
][
rto
]
)
for x, laycol in enumerate(internals.layer_collections.values()):
set_rto(laycol["ptr"], internals.swap_buffer["A"]["RTO"],
internals.swap_buffer["A"]["RTO"]
internals.swap_buffer["B"]["values"][x]
set_rto(laycol["ptr"], internals.swap_buffer["B"]["RTO"],
internals.swap_buffer["B"]["RTO"]
internals.swap_buffer["A"]["values"][x]
# clear rto history
swap_a = internals.swap_buffer["A"]["RTO"]
swap_b = internals.swap_buffer["B"]["RTO"]
internals.rto_history[swap_a].pop(view_layer, None)
internals.rto_history[swap_a+"_all"].pop(view_layer, None)
internals.rto_history[swap_b].pop(view_layer, None)
internals.rto_history[swap_b+"_all"].pop(view_layer, None)
internals.swap_buffer["A"]["RTO"] = ""
internals.swap_buffer["A"]["values"].clear()
internals.swap_buffer["B"]["RTO"] = ""
internals.swap_buffer["B"]["values"].clear()
if internals.copy_buffer["RTO"] == rto:
internals.copy_buffer["RTO"] = ""
internals.copy_buffer["values"].clear()
if internals.swap_buffer["A"]["RTO"] == rto:
internals.swap_buffer["A"]["RTO"] = ""
internals.swap_buffer["A"]["values"].clear()
internals.swap_buffer["B"]["RTO"] = ""
internals.swap_buffer["B"]["values"].clear()
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
def link_child_collections_to_parent(laycol, collection, parent_collection):
# store view layer RTOs for all children of the to be deleted collection
child_states = {}
def get_child_states(layer_collection):
child_states[layer_collection.name] = (layer_collection.exclude,
layer_collection.hide_viewport,
layer_collection.holdout,
layer_collection.indirect_only)
apply_to_children(laycol["ptr"], get_child_states)
# link any subcollections of the to be deleted collection to it's parent
for subcollection in collection.children:
if not subcollection.name in parent_collection.children:
parent_collection.children.link(subcollection)
# apply the stored view layer RTOs to the newly linked collections and their
# children
def restore_child_states(layer_collection):
state = child_states.get(layer_collection.name)
if state:
layer_collection.exclude = state[0]
layer_collection.hide_viewport = state[1]
layer_collection.holdout = state[2]
layer_collection.indirect_only = state[3]
apply_to_children(laycol["parent"]["ptr"], restore_child_states)
def remove_collection(laycol, collection, context):
# get selected row
cm = context.scene.collection_manager
selected_row_name = cm.cm_list_collection[cm.cm_list_index].name
# delete collection
bpy.data.collections.remove(collection)
# update references
internals.expanded.discard(laycol["name"])
if internals.expand_history["target"] == laycol["name"]:
internals.expand_history["target"] = ""
if laycol["name"] in internals.expand_history["history"]:
internals.expand_history["history"].remove(laycol["name"])
if internals.qcd_slots.contains(name=laycol["name"]):
internals.qcd_slots.del_slot(name=laycol["name"])
if laycol["name"] in internals.qcd_slots.overrides:
internals.qcd_slots.overrides.remove(laycol["name"])
for rto in internals.rto_history.values():
rto.clear()
# update tree view
update_property_group(context)
# update selected row
laycol = internals.layer_collections.get(selected_row_name, None)
if laycol:
cm.cm_list_index = laycol["row_index"]
elif len(cm.cm_list_collection) <= cm.cm_list_index:
cm.cm_list_index = len(cm.cm_list_collection) - 1
if cm.cm_list_index > -1:
name = cm.cm_list_collection[cm.cm_list_index].name
laycol = internals.layer_collections[name]
while not laycol["visible"]:
laycol = laycol["parent"]
cm.cm_list_index = laycol["row_index"]
def select_collection_objects(is_master_collection, collection_name, replace, nested, selection_state=None):
if bpy.context.mode != 'OBJECT':
return
target_collection = bpy.context.view_layer.layer_collection.collection
else:
laycol = internals.layer_collections[collection_name]
target_collection = laycol["ptr"].collection
if replace:
bpy.ops.object.select_all(action='DESELECT')
def select_objects(collection, selection_state):
if selection_state == None:
selection_state = get_move_selection().isdisjoint(collection.objects)
for obj in collection.objects:
try:
obj.select_set(selection_state)
except RuntimeError:
pass
select_objects(target_collection, selection_state)
apply_to_children(target_collection, select_objects, selection_state)
def set_exclude_state(target_layer_collection, state):
# get current child exclusion state
child_exclusion = []
def get_child_exclusion(layer_collection):
child_exclusion.append([layer_collection, layer_collection.exclude])
apply_to_children(target_layer_collection, get_child_exclusion)
# set exclusion
target_layer_collection.exclude = state
# set correct state for all children
for laycol in child_exclusion:
laycol[0].exclude = laycol[1]