1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
19 # Copyright 2011, Ryan Inch
23 from . import internals
26 from .internals
import (
27 update_property_group
,
35 'EDIT_SURFACE': 'EDIT',
37 'EDIT_ARMATURE': 'EDIT',
38 'EDIT_METABALL': 'EDIT',
39 'EDIT_LATTICE': 'EDIT',
42 'PAINT_WEIGHT': 'WEIGHT_PAINT',
43 'PAINT_VERTEX': 'VERTEX_PAINT',
44 'PAINT_TEXTURE': 'TEXTURE_PAINT',
45 'PARTICLE': 'PARTICLE_EDIT',
47 'PAINT_GPENCIL': 'PAINT_GPENCIL',
48 'EDIT_GPENCIL': 'EDIT_GPENCIL',
49 'SCULPT_GPENCIL': 'SCULPT_GPENCIL',
50 'WEIGHT_GPENCIL': 'WEIGHT_GPENCIL',
51 'VERTEX_GPENCIL': 'VERTEX_GPENCIL',
57 "select": "collection.hide_select",
58 "hide": "hide_viewport",
59 "disable": "collection.hide_viewport",
60 "render": "collection.hide_render",
62 "indirect": "indirect_only",
119 def get_rto(layer_collection
, rto
):
120 if rto
in ["exclude", "hide", "holdout", "indirect"]:
121 return getattr(layer_collection
, rto_path
[rto
])
124 collection
= getattr(layer_collection
, "collection")
125 return getattr(collection
, rto_path
[rto
].split(".")[1])
128 def set_rto(layer_collection
, rto
, value
):
129 if rto
in ["exclude", "hide", "holdout", "indirect"]:
130 setattr(layer_collection
, rto_path
[rto
], value
)
133 collection
= getattr(layer_collection
, "collection")
134 setattr(collection
, rto_path
[rto
].split(".")[1], value
)
137 def apply_to_children(parent
, apply_function
):
138 # works for both Collections & LayerCollections
139 child_lists
= [parent
.children
]
144 for child_list
in child_lists
:
145 for child
in child_list
:
146 apply_function(child
)
149 new_child_lists
.append(child
.children
)
151 child_lists
= new_child_lists
154 def isolate_rto(cls
, self
, view_layer
, rto
, *, children
=False):
155 off
= set_off_on
[rto
]["off"]
156 on
= set_off_on
[rto
]["on"]
158 laycol_ptr
= internals
.layer_collections
[self
.name
]["ptr"]
159 target
= internals
.rto_history
[rto
][view_layer
]["target"]
160 history
= internals
.rto_history
[rto
][view_layer
]["history"]
162 # get active collections
163 active_layer_collections
= [x
["ptr"] for x
in internals
.layer_collections
.values()
164 if get_rto(x
["ptr"], rto
) == on
]
166 # check if previous state should be restored
167 if cls
.isolated
and self
.name
== target
:
168 # restore previous state
169 for x
, item
in enumerate(internals
.layer_collections
.values()):
170 set_rto(item
["ptr"], rto
, history
[x
])
172 # reset target and history
173 del internals
.rto_history
[rto
][view_layer
]
177 # check if all RTOs should be activated
178 elif (len(active_layer_collections
) == 1 and
179 active_layer_collections
[0].name
== self
.name
):
180 # activate all collections
181 for item
in internals
.layer_collections
.values():
182 set_rto(item
["ptr"], rto
, on
)
184 # reset target and history
185 del internals
.rto_history
[rto
][view_layer
]
192 internals
.rto_history
[rto
][view_layer
]["target"] = self
.name
198 for item
in internals
.layer_collections
.values():
199 history
.append(get_rto(item
["ptr"], rto
))
204 def get_child_states(layer_collection
):
205 child_states
[layer_collection
.name
] = get_rto(layer_collection
, rto
)
207 apply_to_children(laycol_ptr
, get_child_states
)
210 for item
in internals
.layer_collections
.values():
211 if item
["name"] != laycol_ptr
.name
:
212 set_rto(item
["ptr"], rto
, off
)
214 set_rto(laycol_ptr
, rto
, on
)
216 if rto
not in ["exclude", "holdout", "indirect"]:
217 # activate all parents
218 laycol
= internals
.layer_collections
[self
.name
]
219 while laycol
["id"] != 0:
220 set_rto(laycol
["ptr"], rto
, on
)
221 laycol
= laycol
["parent"]
224 # restore child states
225 def restore_child_states(layer_collection
):
226 set_rto(layer_collection
, rto
, child_states
[layer_collection
.name
])
228 apply_to_children(laycol_ptr
, restore_child_states
)
232 # restore child states
233 def restore_child_states(layer_collection
):
234 set_rto(layer_collection
, rto
, child_states
[layer_collection
.name
])
236 apply_to_children(laycol_ptr
, restore_child_states
)
238 elif rto
== "exclude":
239 # deactivate all children
240 def deactivate_all_children(layer_collection
):
241 set_rto(layer_collection
, rto
, True)
243 apply_to_children(laycol_ptr
, deactivate_all_children
)
248 def isolate_sel_objs_collections(view_layer
, rto
, caller
, *, use_active
=False):
249 selected_objects
= get_move_selection()
252 selected_objects
.add(get_move_active(always
=True))
254 if not selected_objects
:
255 return "No selected objects"
257 off
= set_off_on
[rto
]["off"]
258 on
= set_off_on
[rto
]["on"]
261 history
= internals
.rto_history
[rto
+"_all"][view_layer
]
263 elif caller
== "QCD":
264 history
= internals
.qcd_history
[view_layer
]
267 # if not isolated, isolate collections of selected objects
268 if len(history
) == 0:
271 # save history and isolate RTOs
272 for item
in internals
.layer_collections
.values():
273 history
.append(get_rto(item
["ptr"], rto
))
276 # check if any of the selected objects are in the collection
277 if not set(selected_objects
).isdisjoint(item
["ptr"].collection
.objects
):
280 if history
[-1] != rto_state
:
284 set_exclude_state(item
["ptr"], rto_state
)
287 set_rto(item
["ptr"], rto
, rto_state
)
289 # activate all parents if needed
290 if rto_state
== on
and rto
not in ["holdout", "indirect"]:
291 laycol
= item
["parent"]
292 while laycol
["id"] != 0:
293 set_rto(laycol
["ptr"], rto
, on
)
294 laycol
= laycol
["parent"]
300 return "Collection already isolated"
304 for x
, item
in enumerate(internals
.layer_collections
.values()):
305 set_rto(item
["ptr"], rto
, history
[x
])
309 del internals
.rto_history
[rto
+"_all"][view_layer
]
311 elif caller
== "QCD":
312 del internals
.qcd_history
[view_layer
]
315 def disable_sel_objs_collections(view_layer
, rto
, caller
):
316 off
= set_off_on
[rto
]["off"]
317 on
= set_off_on
[rto
]["on"]
318 selected_objects
= get_move_selection()
321 history
= internals
.rto_history
[rto
+"_all"][view_layer
]
323 elif caller
== "QCD":
324 history
= internals
.qcd_history
[view_layer
]
327 if not selected_objects
and not history
:
330 del internals
.rto_history
[rto
+"_all"][view_layer
]
332 elif caller
== "QCD":
333 del internals
.qcd_history
[view_layer
]
335 return "No selected objects"
337 # if not disabled, disable collections of selected objects
338 if len(history
) == 0:
339 # save history and disable RTOs
340 for item
in internals
.layer_collections
.values():
341 history
.append(get_rto(item
["ptr"], rto
))
343 # check if any of the selected objects are in the collection
344 if not set(selected_objects
).isdisjoint(item
["ptr"].collection
.objects
):
346 set_exclude_state(item
["ptr"], off
)
349 set_rto(item
["ptr"], rto
, off
)
353 for x
, item
in enumerate(internals
.layer_collections
.values()):
354 set_rto(item
["ptr"], rto
, history
[x
])
358 del internals
.rto_history
[rto
+"_all"][view_layer
]
360 elif caller
== "QCD":
361 del internals
.qcd_history
[view_layer
]
364 def toggle_children(self
, view_layer
, rto
):
365 laycol_ptr
= internals
.layer_collections
[self
.name
]["ptr"]
367 del internals
.rto_history
[rto
][view_layer
]
368 internals
.rto_history
[rto
+"_all"].pop(view_layer
, None)
371 state
= not get_rto(laycol_ptr
, rto
)
372 set_rto(laycol_ptr
, rto
, state
)
374 def set_state(layer_collection
):
375 set_rto(layer_collection
, rto
, state
)
377 apply_to_children(laycol_ptr
, set_state
)
380 def activate_all_rtos(view_layer
, rto
):
381 off
= set_off_on
[rto
]["off"]
382 on
= set_off_on
[rto
]["on"]
384 history
= internals
.rto_history
[rto
+"_all"][view_layer
]
386 # if not activated, activate all
387 if len(history
) == 0:
390 for item
in reversed(list(internals
.layer_collections
.values())):
391 if get_rto(item
["ptr"], rto
) == off
:
394 history
.append(get_rto(item
["ptr"], rto
))
396 set_rto(item
["ptr"], rto
, on
)
404 for x
, item
in enumerate(internals
.layer_collections
.values()):
405 set_rto(item
["ptr"], rto
, history
[x
])
408 del internals
.rto_history
[rto
+"_all"][view_layer
]
411 def invert_rtos(view_layer
, rto
):
415 for item
in internals
.layer_collections
.values():
416 orig_values
.append(get_rto(item
["ptr"], rto
))
418 for x
, item
in enumerate(internals
.layer_collections
.values()):
419 set_rto(item
["ptr"], rto
, not orig_values
[x
])
422 for item
in internals
.layer_collections
.values():
423 set_rto(item
["ptr"], rto
, not get_rto(item
["ptr"], rto
))
426 internals
.rto_history
[rto
].pop(view_layer
, None)
429 def copy_rtos(view_layer
, rto
):
430 if not internals
.copy_buffer
["RTO"]:
432 internals
.copy_buffer
["RTO"] = rto
433 for laycol
in internals
.layer_collections
.values():
434 internals
.copy_buffer
["values"].append(get_off_on
[
435 get_rto(laycol
["ptr"], rto
)
443 for x
, laycol
in enumerate(internals
.layer_collections
.values()):
444 set_rto(laycol
["ptr"],
447 internals
.copy_buffer
["values"][x
]
452 internals
.rto_history
[rto
].pop(view_layer
, None)
453 del internals
.rto_history
[rto
+"_all"][view_layer
]
456 internals
.copy_buffer
["RTO"] = ""
457 internals
.copy_buffer
["values"].clear()
460 def swap_rtos(view_layer
, rto
):
461 if not internals
.swap_buffer
["A"]["values"]:
463 internals
.swap_buffer
["A"]["RTO"] = rto
464 for laycol
in internals
.layer_collections
.values():
465 internals
.swap_buffer
["A"]["values"].append(get_off_on
[
466 get_rto(laycol
["ptr"], rto
)
474 internals
.swap_buffer
["B"]["RTO"] = rto
475 for laycol
in internals
.layer_collections
.values():
476 internals
.swap_buffer
["B"]["values"].append(get_off_on
[
477 get_rto(laycol
["ptr"], rto
)
484 for x
, laycol
in enumerate(internals
.layer_collections
.values()):
485 set_rto(laycol
["ptr"], internals
.swap_buffer
["A"]["RTO"],
487 internals
.swap_buffer
["A"]["RTO"]
489 internals
.swap_buffer
["B"]["values"][x
]
493 set_rto(laycol
["ptr"], internals
.swap_buffer
["B"]["RTO"],
495 internals
.swap_buffer
["B"]["RTO"]
497 internals
.swap_buffer
["A"]["values"][x
]
503 swap_a
= internals
.swap_buffer
["A"]["RTO"]
504 swap_b
= internals
.swap_buffer
["B"]["RTO"]
506 internals
.rto_history
[swap_a
].pop(view_layer
, None)
507 internals
.rto_history
[swap_a
+"_all"].pop(view_layer
, None)
508 internals
.rto_history
[swap_b
].pop(view_layer
, None)
509 internals
.rto_history
[swap_b
+"_all"].pop(view_layer
, None)
512 internals
.swap_buffer
["A"]["RTO"] = ""
513 internals
.swap_buffer
["A"]["values"].clear()
514 internals
.swap_buffer
["B"]["RTO"] = ""
515 internals
.swap_buffer
["B"]["values"].clear()
519 if internals
.copy_buffer
["RTO"] == rto
:
520 internals
.copy_buffer
["RTO"] = ""
521 internals
.copy_buffer
["values"].clear()
525 if internals
.swap_buffer
["A"]["RTO"] == rto
:
526 internals
.swap_buffer
["A"]["RTO"] = ""
527 internals
.swap_buffer
["A"]["values"].clear()
528 internals
.swap_buffer
["B"]["RTO"] = ""
529 internals
.swap_buffer
["B"]["values"].clear()
532 def link_child_collections_to_parent(laycol
, collection
, parent_collection
):
533 # store view layer RTOs for all children of the to be deleted collection
535 def get_child_states(layer_collection
):
536 child_states
[layer_collection
.name
] = (layer_collection
.exclude
,
537 layer_collection
.hide_viewport
,
538 layer_collection
.holdout
,
539 layer_collection
.indirect_only
)
541 apply_to_children(laycol
["ptr"], get_child_states
)
543 # link any subcollections of the to be deleted collection to it's parent
544 for subcollection
in collection
.children
:
545 if not subcollection
.name
in parent_collection
.children
:
546 parent_collection
.children
.link(subcollection
)
548 # apply the stored view layer RTOs to the newly linked collections and their
550 def restore_child_states(layer_collection
):
551 state
= child_states
.get(layer_collection
.name
)
554 layer_collection
.exclude
= state
[0]
555 layer_collection
.hide_viewport
= state
[1]
556 layer_collection
.holdout
= state
[2]
557 layer_collection
.indirect_only
= state
[3]
559 apply_to_children(laycol
["parent"]["ptr"], restore_child_states
)
562 def remove_collection(laycol
, collection
, context
):
564 cm
= context
.scene
.collection_manager
565 selected_row_name
= cm
.cm_list_collection
[cm
.cm_list_index
].name
568 bpy
.data
.collections
.remove(collection
)
571 internals
.expanded
.discard(laycol
["name"])
573 if internals
.expand_history
["target"] == laycol
["name"]:
574 internals
.expand_history
["target"] = ""
576 if laycol
["name"] in internals
.expand_history
["history"]:
577 internals
.expand_history
["history"].remove(laycol
["name"])
579 if internals
.qcd_slots
.contains(name
=laycol
["name"]):
580 internals
.qcd_slots
.del_slot(name
=laycol
["name"])
582 if laycol
["name"] in internals
.qcd_slots
.overrides
:
583 internals
.qcd_slots
.overrides
.remove(laycol
["name"])
586 for rto
in internals
.rto_history
.values():
590 update_property_group(context
)
592 # update selected row
593 laycol
= internals
.layer_collections
.get(selected_row_name
, None)
595 cm
.cm_list_index
= laycol
["row_index"]
597 elif len(cm
.cm_list_collection
) <= cm
.cm_list_index
:
598 cm
.cm_list_index
= len(cm
.cm_list_collection
) - 1
600 if cm
.cm_list_index
> -1:
601 name
= cm
.cm_list_collection
[cm
.cm_list_index
].name
602 laycol
= internals
.layer_collections
[name
]
603 while not laycol
["visible"]:
604 laycol
= laycol
["parent"]
606 cm
.cm_list_index
= laycol
["row_index"]
609 def select_collection_objects(is_master_collection
, collection_name
, replace
, nested
, selection_state
=None):
610 if bpy
.context
.mode
!= 'OBJECT':
613 if is_master_collection
:
614 target_collection
= bpy
.context
.view_layer
.layer_collection
.collection
617 laycol
= internals
.layer_collections
[collection_name
]
618 target_collection
= laycol
["ptr"].collection
621 bpy
.ops
.object.select_all(action
='DESELECT')
623 if selection_state
== None:
624 selection_state
= get_move_selection().isdisjoint(target_collection
.objects
)
626 def select_objects(collection
):
627 for obj
in collection
.objects
:
629 obj
.select_set(selection_state
)
633 select_objects(target_collection
)
636 apply_to_children(target_collection
, select_objects
)
638 def set_exclude_state(target_layer_collection
, state
):
639 # get current child exclusion state
642 def get_child_exclusion(layer_collection
):
643 child_exclusion
.append([layer_collection
, layer_collection
.exclude
])
645 apply_to_children(target_layer_collection
, get_child_exclusion
)
649 target_layer_collection
.exclude
= state
652 # set correct state for all children
653 for laycol
in child_exclusion
:
654 laycol
[0].exclude
= laycol
[1]