Outliner visibility unification: Implement 3 levels of viewport visibility

Now collection and objects can be either:
* Disabled for all the view layers.
* Hidden for a view layer but not necessarily for all others.
* Visible for a view layer but not necessarily for all others.

Regarding icons: Whatever we decide to use for the "Hidden for all view
layers" needs to be a toggle-like icon. Because when viewing "Scenes"
instead of "View Layer" in the outliner we should be able to edit the
collection "Hidden for all the view layers" as an on/off option.

The operators are accessible via a Visibility context menu or shortcuts:
* Ctrl + Click: Isolate collection (use shift to extend).
* Alt + Click: Disable collection.
* Shift + Click: Hide/Show collection and its children (objects and collections)

Things yet to be tackled:
* Object outliner context menu can also get a Visibility sub-menu.
* Get better icons for viewport enable/disable.

Note:
* When using emulate 3 button mouse alt+click is used for 2d panning.
  In this case users have to use the operator from the menu.

See T57857 for discussion.

Patch: https://developer.blender.org/D4011
Reviewers: brecht and sergey

Thanks to the reviewers and William Reynish and Julien Kasper in
particular for the feedback.
This commit is contained in:
Dalai Felinto 2018-11-30 02:24:06 -02:00
parent e3f7f0c3eb
commit 897e047374
12 changed files with 645 additions and 124 deletions

@ -148,6 +148,29 @@ class OUTLINER_MT_collection_view_layer(Menu):
layout.operator("outliner.collection_holdout_clear")
class OUTLINER_MT_collection_visibility(Menu):
bl_label = "Visibility"
def draw(self, context):
layout = self.layout
layout.operator("outliner.collection_isolate", text="Isolate")
layout.operator("outliner.collection_show", text="Show")
layout.operator("outliner.collection_hide", text="Hide")
layout.separator()
layout.operator("outliner.collection_show_inside", text="Show All Inside")
layout.operator("outliner.collection_hide_inside", text="Hide All Inside")
layout.separator()
layout.operator("outliner.collection_enable", text="Enable in Viewports")
layout.operator("outliner.collection_disable", text="Disable in Viewports")
layout.separator()
layout.operator("outliner.collection_enable_render", text="Enable in Render")
layout.operator("outliner.collection_disable_render", text="Disable in Render")
class OUTLINER_MT_collection(Menu):
bl_label = "Collection"
@ -177,6 +200,9 @@ class OUTLINER_MT_collection(Menu):
layout.separator()
layout.menu("OUTLINER_MT_collection_view_layer")
layout.separator()
layout.menu("OUTLINER_MT_collection_visibility")
layout.separator()
layout.operator_menu_enum("outliner.id_operation", "type", text="ID Data")
@ -301,6 +327,7 @@ classes = (
OUTLINER_MT_edit_datablocks,
OUTLINER_MT_collection,
OUTLINER_MT_collection_new,
OUTLINER_MT_collection_visibility,
OUTLINER_MT_collection_view_layer,
OUTLINER_MT_object,
OUTLINER_MT_context,

@ -109,7 +109,8 @@ bool BKE_layer_collection_has_layer_collection(
struct LayerCollection *lc_parent, struct LayerCollection *lc_child);
void BKE_base_set_visible(struct Scene *scene, struct ViewLayer *view_layer, struct Base *base, bool extend);
void BKE_layer_collection_set_visible(struct Scene *scene, struct ViewLayer *view_layer, struct LayerCollection *lc, bool extend);
bool BKE_layer_collection_isolate(struct Scene *scene, struct ViewLayer *view_layer, struct LayerCollection *lc, bool extend);
bool BKE_layer_collection_set_visible(struct ViewLayer *view_layer, struct LayerCollection *lc, const bool visible, const bool hierarchy);
/* evaluation */

@ -669,7 +669,9 @@ static short layer_collection_sync(
lc->runtime_flag = child_runtime_flag;
}
if ((child_layer_restrict & LAYER_COLLECTION_RESTRICT_VIEW) == 0) {
if (((child_restrict & COLLECTION_RESTRICT_VIEW) == 0) &&
((child_layer_restrict & LAYER_COLLECTION_RESTRICT_VIEW) == 0))
{
lc->runtime_flag |= LAYER_COLLECTION_VISIBLE;
}
@ -701,15 +703,18 @@ static short layer_collection_sync(
int object_restrict = base->object->restrictflag;
if (((child_restrict & COLLECTION_RESTRICT_VIEW) == 0) &&
((child_layer_restrict & LAYER_COLLECTION_RESTRICT_VIEW) == 0) &&
((object_restrict & OB_RESTRICT_VIEW) == 0))
{
base->flag |= BASE_VISIBLE | BASE_ENABLED | BASE_ENABLED_VIEWPORT;
base->flag |= BASE_ENABLED | BASE_ENABLED_VIEWPORT;
if (((child_restrict & COLLECTION_RESTRICT_SELECT) == 0) &&
((object_restrict & OB_RESTRICT_SELECT) == 0))
{
base->flag |= BASE_SELECTABLE;
if ((child_layer_restrict & LAYER_COLLECTION_RESTRICT_VIEW) == 0) {
base->flag |= BASE_VISIBLE;
if (((child_restrict & COLLECTION_RESTRICT_SELECT) == 0) &&
((object_restrict & OB_RESTRICT_SELECT) == 0))
{
base->flag |= BASE_SELECTABLE;
}
}
}
@ -720,7 +725,7 @@ static short layer_collection_sync(
}
/* Update runtime flags used for display and tools. */
if (base->flag & BASE_VISIBLE) {
if (base->flag & BASE_ENABLED) {
lc->runtime_flag |= LAYER_COLLECTION_HAS_ENABLED_OBJECTS;
}
@ -987,48 +992,124 @@ static void layer_collection_flag_unset_recursive(LayerCollection *lc, const int
}
/**
* Set collection per-view layer visiblity.
* When not extending, we show all the direct parents and all children of the layer collection.
*/
void BKE_layer_collection_set_visible(Scene *scene, ViewLayer *view_layer, LayerCollection *lc, bool extend)
* Return true if something changed. */
static bool layer_collection_collection_flag_unset_recursive(LayerCollection *lc, const int flag)
{
bool changed = (lc->collection->flag & flag) != 0;
lc->collection->flag &= ~flag;
for (LayerCollection *lc_iter = lc->layer_collections.first; lc_iter; lc_iter = lc_iter->next) {
changed |= layer_collection_collection_flag_unset_recursive(lc_iter, flag);
}
return changed;
}
/**
* Isolate the collection - hide all other collections but this one.
* Make sure to show all the direct parents and all children of the layer collection as well.
* When extending we simply show the collections and its direct family.
*
* Return whether depsgraph needs update.
*/
bool BKE_layer_collection_isolate(Scene *scene, ViewLayer *view_layer, LayerCollection *lc, bool extend)
{
bool depsgraph_need_update = false;
if (!extend) {
/* Make only this collection visible. */
/* Hide all collections . */
for (LayerCollection *lc_iter = view_layer->layer_collections.first; lc_iter; lc_iter = lc_iter->next) {
layer_collection_flag_set_recursive(lc_iter, LAYER_COLLECTION_RESTRICT_VIEW);
}
}
/* Make all the direct parents visible. */
LayerCollection *lc_parent = lc;
LayerCollection *lc_master = view_layer->layer_collections.first;
for (LayerCollection *lc_iter = lc_master->layer_collections.first; lc_iter; lc_iter = lc_iter->next) {
/* Make all the direct parents visible. */
LayerCollection *lc_parent = lc;
LayerCollection *lc_master = view_layer->layer_collections.first;
for (LayerCollection *lc_iter = lc_master->layer_collections.first; lc_iter; lc_iter = lc_iter->next) {
if (BKE_layer_collection_has_layer_collection(lc_iter, lc)) {
lc_parent = lc_iter;
break;
}
}
while (lc_parent != lc) {
depsgraph_need_update |= (lc_parent->collection->flag & COLLECTION_RESTRICT_VIEW) != 0;
lc_parent->collection->flag &= ~COLLECTION_RESTRICT_VIEW;
lc_parent->flag &= ~LAYER_COLLECTION_RESTRICT_VIEW;
for (LayerCollection *lc_iter = lc_parent->layer_collections.first; lc_iter; lc_iter = lc_iter->next) {
if (BKE_layer_collection_has_layer_collection(lc_iter, lc)) {
lc_parent = lc_iter;
break;
}
}
while (lc_parent != lc) {
lc_parent->flag &= ~LAYER_COLLECTION_RESTRICT_VIEW;
for (LayerCollection *lc_iter = lc_parent->layer_collections.first; lc_iter; lc_iter = lc_iter->next) {
if (BKE_layer_collection_has_layer_collection(lc_iter, lc)) {
lc_parent = lc_iter;
break;
}
}
}
/* Make all the children visible. */
layer_collection_flag_unset_recursive(lc, LAYER_COLLECTION_RESTRICT_VIEW);
BKE_layer_collection_activate(view_layer, lc);
}
else {
lc->flag ^= LAYER_COLLECTION_RESTRICT_VIEW;
}
/* Make all the children visible. */
layer_collection_flag_unset_recursive(lc, LAYER_COLLECTION_RESTRICT_VIEW);
depsgraph_need_update |= layer_collection_collection_flag_unset_recursive(lc, COLLECTION_RESTRICT_VIEW);
BKE_layer_collection_activate(view_layer, lc);
BKE_layer_collection_sync(scene, view_layer);
return depsgraph_need_update;
}
static void layer_collection_bases_show_recursive(ViewLayer *view_layer, LayerCollection *lc)
{
for (CollectionObject *cob = lc->collection->gobject.first; cob; cob = cob->next) {
Base *base = BKE_view_layer_base_find(view_layer, cob->ob);
base->flag &= ~BASE_HIDDEN;
base->object->restrictflag &= ~OB_RESTRICT_VIEW;
}
for (LayerCollection *lc_iter = lc->layer_collections.first; lc_iter; lc_iter = lc_iter->next) {
layer_collection_bases_show_recursive(view_layer, lc_iter);
}
}
static void layer_collection_bases_hide_recursive(ViewLayer *view_layer, LayerCollection *lc)
{
for (CollectionObject *cob = lc->collection->gobject.first; cob; cob = cob->next) {
Base *base = BKE_view_layer_base_find(view_layer, cob->ob);
base->flag |= BASE_HIDDEN;
}
for (LayerCollection *lc_iter = lc->layer_collections.first; lc_iter; lc_iter = lc_iter->next) {
layer_collection_bases_hide_recursive(view_layer, lc_iter);
}
}
/**
* Hide/show all the elements of a collection.
* Enable a disable collection if needs be.
*
* Return true if depsgraph needs update.
*/
bool BKE_layer_collection_set_visible(ViewLayer *view_layer, LayerCollection *lc, const bool visible, const bool hierarchy)
{
bool depsgraph_changed = false;
if (hierarchy) {
if (visible) {
depsgraph_changed |= layer_collection_collection_flag_unset_recursive(lc, COLLECTION_RESTRICT_VIEW);
layer_collection_flag_unset_recursive(lc, LAYER_COLLECTION_RESTRICT_VIEW);
layer_collection_bases_show_recursive(view_layer, lc);
}
else {
layer_collection_flag_set_recursive(lc, LAYER_COLLECTION_RESTRICT_VIEW);
layer_collection_bases_hide_recursive(view_layer, lc);
}
}
else {
if (visible) {
depsgraph_changed |= (lc->collection->flag & COLLECTION_RESTRICT_VIEW) != 0;
lc->flag &= ~LAYER_COLLECTION_RESTRICT_VIEW;
lc->collection->flag &= ~COLLECTION_RESTRICT_VIEW;
}
else {
lc->flag |= LAYER_COLLECTION_RESTRICT_VIEW;
}
}
return depsgraph_changed;
}
/* ---------------------------------------------------------------------- */
@ -1384,7 +1465,7 @@ void BKE_layer_eval_view_layer(
/* Visibility based on depsgraph mode. */
const eEvaluationMode mode = DEG_get_mode(depsgraph);
const int base_visible_flag = (mode == DAG_EVAL_VIEWPORT)
const int base_enabled_flag = (mode == DAG_EVAL_VIEWPORT)
? BASE_ENABLED_VIEWPORT
: BASE_ENABLED_RENDER;
/* Create array of bases, for fast index-based lookup. */
@ -1395,10 +1476,12 @@ void BKE_layer_eval_view_layer(
int base_index = 0;
for (Base *base = view_layer->object_bases.first; base; base = base->next) {
/* Compute visibility for depsgraph evaluation mode. */
if (base->flag & base_visible_flag) {
base->flag |= BASE_ENABLED | BASE_VISIBLE;
if (mode == DAG_EVAL_VIEWPORT && (base->flag & BASE_HIDDEN)) {
base->flag &= ~BASE_VISIBLE;
if (base->flag & base_enabled_flag) {
base->flag |= BASE_ENABLED;
/* When rendering, visibility is controlled by the enable/disable option. */
if (mode == DAG_EVAL_RENDER) {
base->flag |= BASE_VISIBLE;
}
}
else {
@ -1416,7 +1499,7 @@ void BKE_layer_eval_view_layer(
Base *base_orig = view_layer_orig->object_bases.first;
const Base *base_eval = view_layer->object_bases.first;
while (base_orig != NULL) {
if (base_orig->flag & base_visible_flag) {
if (base_orig->flag & base_enabled_flag) {
base_orig->flag = base_eval->flag;
base_eval = base_eval->next;
}

@ -64,11 +64,6 @@ void DepsgraphNodeBuilder::build_layer_collections(ListBase *lb)
COLLECTION_RESTRICT_VIEW : COLLECTION_RESTRICT_RENDER;
for (LayerCollection *lc = (LayerCollection *)lb->first; lc; lc = lc->next) {
if ((graph_->mode == DAG_EVAL_VIEWPORT) &&
((lc->flag & LAYER_COLLECTION_RESTRICT_VIEW) != 0))
{
continue;
}
if (lc->collection->flag & restrict_flag) {
continue;
}

@ -65,11 +65,6 @@ void DepsgraphRelationBuilder::build_layer_collections(ListBase *lb)
COLLECTION_RESTRICT_VIEW : COLLECTION_RESTRICT_RENDER;
for (LayerCollection *lc = (LayerCollection *)lb->first; lc; lc = lc->next) {
if ((graph_->mode == DAG_EVAL_VIEWPORT) &&
((lc->flag & LAYER_COLLECTION_RESTRICT_VIEW) != 0))
{
continue;
}
if ((lc->collection->flag & restrict_flag)) {
continue;
}

@ -359,23 +359,23 @@ void scene_remove_unused_view_layers(const Depsgraph *depsgraph,
scene_cow->view_layers.last = view_layer_eval;
}
/* Makes it so given view layer only has bases corresponding to a visible
/* Makes it so given view layer only has bases corresponding to enabled
* objects. */
void view_layer_remove_invisible_bases(const Depsgraph *depsgraph,
ViewLayer *view_layer)
void view_layer_remove_disabled_bases(const Depsgraph *depsgraph,
ViewLayer *view_layer)
{
const int base_visible_flag = (depsgraph->mode == DAG_EVAL_VIEWPORT) ?
const int base_enabled_flag = (depsgraph->mode == DAG_EVAL_VIEWPORT) ?
BASE_ENABLED_VIEWPORT : BASE_ENABLED_RENDER;
ListBase visible_bases = {NULL, NULL};
ListBase enabled_bases = {NULL, NULL};
for (Base *base = reinterpret_cast<Base *>(view_layer->object_bases.first),
*base_next;
base != NULL;
base = base_next)
{
base_next = base->next;
const bool is_object_visible = (base->flag & base_visible_flag);
if (is_object_visible) {
BLI_addtail(&visible_bases, base);
const bool is_object_enabled = (base->flag & base_enabled_flag);
if (is_object_enabled) {
BLI_addtail(&enabled_bases, base);
}
else {
if (base == view_layer->basact) {
@ -384,13 +384,13 @@ void view_layer_remove_invisible_bases(const Depsgraph *depsgraph,
MEM_freeN(base);
}
}
view_layer->object_bases = visible_bases;
view_layer->object_bases = enabled_bases;
}
void scene_cleanup_view_layers(const Depsgraph *depsgraph, Scene *scene_cow)
{
scene_remove_unused_view_layers(depsgraph, scene_cow);
view_layer_remove_invisible_bases(
view_layer_remove_disabled_bases(
depsgraph,
reinterpret_cast<ViewLayer *>(scene_cow->view_layers.first));
/* TODO(sergey): Remove objects from collections as well.

@ -283,10 +283,12 @@ static int object_hide_collection_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
BKE_layer_collection_set_visible(scene, view_layer, lc, extend);
DEG_relations_tag_update(CTX_data_main(C));
DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS);
if (BKE_layer_collection_isolate(scene, view_layer, lc, extend)) {
DEG_relations_tag_update(CTX_data_main(C));
}
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
return OPERATOR_FINISHED;

@ -820,6 +820,348 @@ void OUTLINER_OT_collection_indirect_only_clear(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/************************** Visibility Operators ******************************/
static int collection_isolate_exec(bContext *C, wmOperator *op) {
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
SpaceOops *soops = CTX_wm_space_outliner(C);
const bool extend = RNA_boolean_get(op->ptr, "extend");
bool depsgraph_changed = false;
struct CollectionEditData data = {.scene = scene, .soops = soops,};
data.collections_to_edit = BLI_gset_ptr_new(__func__);
/* Hide all collections before the isolate function - needed in order to support. */
if (!extend) {
for (LayerCollection *lc_iter = view_layer->layer_collections.first; lc_iter; lc_iter = lc_iter->next) {
lc_iter->flag |= LAYER_COLLECTION_RESTRICT_VIEW;
layer_collection_flag_recursive_set(lc_iter, LAYER_COLLECTION_RESTRICT_VIEW);
}
}
outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, layer_collection_find_data_to_edit, &data);
GSetIterator collections_to_edit_iter;
GSET_ITER(collections_to_edit_iter, data.collections_to_edit) {
LayerCollection *layer_collection = BLI_gsetIterator_getKey(&collections_to_edit_iter);
depsgraph_changed |= BKE_layer_collection_isolate(scene, view_layer, layer_collection, true);
}
BLI_gset_free(data.collections_to_edit, NULL);
BKE_layer_collection_sync(scene, view_layer);
DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS);
if (depsgraph_changed) {
DEG_relations_tag_update(CTX_data_main(C));
}
WM_main_add_notifier(NC_SCENE | ND_LAYER_CONTENT, NULL);
return OPERATOR_FINISHED;
}
static int collection_isolate_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
PropertyRNA *prop = RNA_struct_find_property(op->ptr, "extend");
if (!RNA_property_is_set(op->ptr, prop) && (event->shift)) {
RNA_property_boolean_set(op->ptr, prop, true);
}
return collection_isolate_exec(C, op);
}
void OUTLINER_OT_collection_isolate(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Isolate Collection";
ot->idname = "OUTLINER_OT_collection_isolate";
ot->description = "Hide all but this collection and its parents";
/* api callbacks */
ot->exec = collection_isolate_exec;
ot->invoke = collection_isolate_invoke;
ot->poll = ED_outliner_collections_editor_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
PropertyRNA *prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend current visible collections");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
static bool collection_show_poll(bContext *C)
{
return collections_view_layer_poll(C, true, LAYER_COLLECTION_RESTRICT_VIEW);
}
static bool collection_hide_poll(bContext *C)
{
return collections_view_layer_poll(C, false, LAYER_COLLECTION_RESTRICT_VIEW);
}
static bool collection_inside_poll(bContext *C)
{
if (!ED_outliner_collections_editor_poll(C)) {
return false;
}
return outliner_active_layer_collection(C) != NULL;
}
static int collection_visibility_exec(bContext *C, wmOperator *op) {
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
SpaceOops *soops = CTX_wm_space_outliner(C);
const bool is_inside = strstr(op->idname, "inside") != NULL;
const bool show = strstr(op->idname, "show") != NULL;
bool depsgraph_changed = false;
struct CollectionEditData data = {.scene = scene, .soops = soops,};
data.collections_to_edit = BLI_gset_ptr_new(__func__);
outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, layer_collection_find_data_to_edit, &data);
GSetIterator collections_to_edit_iter;
GSET_ITER(collections_to_edit_iter, data.collections_to_edit) {
LayerCollection *layer_collection = BLI_gsetIterator_getKey(&collections_to_edit_iter);
depsgraph_changed |= BKE_layer_collection_set_visible(view_layer, layer_collection, show, is_inside);
}
BLI_gset_free(data.collections_to_edit, NULL);
BKE_layer_collection_sync(scene, view_layer);
DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS);
if (depsgraph_changed) {
DEG_relations_tag_update(CTX_data_main(C));
}
WM_main_add_notifier(NC_SCENE | ND_LAYER_CONTENT, NULL);
return OPERATOR_FINISHED;
}
void OUTLINER_OT_collection_show(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Show Collection";
ot->idname = "OUTLINER_OT_collection_show";
ot->description = "Show the collection in this view layer";
/* api callbacks */
ot->exec = collection_visibility_exec;
ot->poll = collection_show_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
void OUTLINER_OT_collection_hide(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Hide Collection";
ot->idname = "OUTLINER_OT_collection_hide";
ot->description = "Hide the collection in this view layer";
/* api callbacks */
ot->exec = collection_visibility_exec;
ot->poll = collection_hide_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
void OUTLINER_OT_collection_show_inside(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Show Inside Collection";
ot->idname = "OUTLINER_OT_collection_show_inside";
ot->description = "Show all the objects and collections inside the collection";
/* api callbacks */
ot->exec = collection_visibility_exec;
ot->poll = collection_inside_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
void OUTLINER_OT_collection_hide_inside(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Hide Inside Collection";
ot->idname = "OUTLINER_OT_collection_hide_inside";
ot->description = "Hide all the objects and collections inside the collection";
/* api callbacks */
ot->exec = collection_visibility_exec;
ot->poll = collection_inside_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static bool collection_flag_poll(bContext *C, bool clear, int flag)
{
if (!ED_outliner_collections_editor_poll(C)) {
return false;
}
TreeElement *te = outliner_active_collection(C);
if (te == NULL) {
return false;
}
Collection *collection = outliner_collection_from_tree_element(te);
if (collection == NULL) {
return false;
}
if (clear && (collection->flag & flag)) {
return true;
}
else if (!clear && !(collection->flag & flag)) {
return true;
}
return false;
}
static bool collection_enable_poll(bContext *C)
{
return collection_flag_poll(C, true, COLLECTION_RESTRICT_VIEW);
}
static bool collection_disable_poll(bContext *C)
{
return collection_flag_poll(C, false, COLLECTION_RESTRICT_VIEW);
}
static bool collection_enable_render_poll(bContext *C)
{
return collection_flag_poll(C, true, COLLECTION_RESTRICT_RENDER);
}
static bool collection_disable_render_poll(bContext *C)
{
return collection_flag_poll(C, false, COLLECTION_RESTRICT_RENDER);
}
static int collection_flag_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
SpaceOops *soops = CTX_wm_space_outliner(C);
const bool is_render = strstr(op->idname, "render");
const bool clear = strstr(op->idname, "show") || strstr(op->idname, "enable");
int flag = is_render ? COLLECTION_RESTRICT_RENDER : COLLECTION_RESTRICT_VIEW;
struct CollectionEditData data = {.scene = scene, .soops = soops,};
data.collections_to_edit = BLI_gset_ptr_new(__func__);
const bool has_layer_collection = soops->outlinevis == SO_VIEW_LAYER;
if (has_layer_collection) {
outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, layer_collection_find_data_to_edit, &data);
GSetIterator collections_to_edit_iter;
GSET_ITER(collections_to_edit_iter, data.collections_to_edit) {
LayerCollection *layer_collection = BLI_gsetIterator_getKey(&collections_to_edit_iter);
Collection *collection = layer_collection->collection;
if (clear) {
collection->flag &= ~flag;
}
else {
collection->flag |= flag;
}
/* Make sure (at least for this view layer) the collection is visible. */
if (clear && !is_render) {
layer_collection->flag &= ~LAYER_COLLECTION_RESTRICT_VIEW;
}
}
BLI_gset_free(data.collections_to_edit, NULL);
}
else {
outliner_tree_traverse(soops, &soops->tree, 0, TSE_SELECTED, collection_find_data_to_edit, &data);
GSetIterator collections_to_edit_iter;
GSET_ITER(collections_to_edit_iter, data.collections_to_edit) {
Collection *collection = BLI_gsetIterator_getKey(&collections_to_edit_iter);
if (clear) {
collection->flag &= ~flag;
}
else {
collection->flag |= flag;
}
}
BLI_gset_free(data.collections_to_edit, NULL);
}
BKE_layer_collection_sync(scene, view_layer);
DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS);
if (!is_render) {
DEG_relations_tag_update(CTX_data_main(C));
}
WM_main_add_notifier(NC_SCENE | ND_LAYER_CONTENT, NULL);
return OPERATOR_FINISHED;
}
void OUTLINER_OT_collection_enable(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Enable Collection";
ot->idname = "OUTLINER_OT_collection_enable";
ot->description = "Enable viewport drawing in the view layers";
/* api callbacks */
ot->exec = collection_flag_exec;
ot->poll = collection_enable_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
void OUTLINER_OT_collection_disable(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Disable Collection";
ot->idname = "OUTLINER_OT_collection_disable";
ot->description = "Disable viewport drawing in the view layers";
/* api callbacks */
ot->exec = collection_flag_exec;
ot->poll = collection_disable_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
void OUTLINER_OT_collection_enable_render(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Enable Collection in Render";
ot->idname = "OUTLINER_OT_collection_enable_render";
ot->description = "Render the collection";
/* api callbacks */
ot->exec = collection_flag_exec;
ot->poll = collection_enable_render_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
void OUTLINER_OT_collection_disable_render(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Disable Collection in Render";
ot->idname = "OUTLINER_OT_collection_disable_render";
ot->description = "Do not render this collection";
/* api callbacks */
ot->exec = collection_flag_exec;
ot->poll = collection_disable_render_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/**
* Populates the \param objects: ListBase with all the outliner selected objects
* We store it as (Object *)LinkData->data

@ -267,41 +267,72 @@ static void restrictbutton_id_user_toggle(bContext *UNUSED(C), void *poin, void
static void hidebutton_base_flag_cb(bContext *C, void *poin, void *poin2)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = poin;
Base *base = poin2;
bool extend = (CTX_wm_window(C)->eventstate->ctrl == 0);
Object *ob = base->object;
bool freeze = (CTX_wm_window(C)->eventstate->alt != 0);
bool changed_restrict_view = false;
/* Undo button toggle, let function do it. */
base->flag ^= BASE_HIDDEN;
BKE_base_set_visible(scene, view_layer, base, extend);
if (!extend && (base->flag & BASE_VISIBLE)) {
/* Auto select solo-ed object. */
ED_object_base_select(base, BA_SELECT);
view_layer->basact = base;
if (freeze) {
ob->restrictflag |= OB_RESTRICT_VIEW;
changed_restrict_view = true;
}
else if (ob->restrictflag & OB_RESTRICT_VIEW) {
ob->restrictflag &= ~OB_RESTRICT_VIEW;
base->flag &= ~BASE_HIDDEN;
changed_restrict_view = true;
}
else {
base->flag ^= BASE_HIDDEN;
}
DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS);
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
if (changed_restrict_view) {
BKE_main_collection_sync(bmain);
DEG_id_tag_update(&ob->id, LIB_TAG_COPIED_ON_WRITE);
DEG_relations_tag_update(bmain);
WM_main_add_notifier(NC_OBJECT | ND_DRAW, &ob->id);
}
if (!freeze) {
BKE_layer_collection_sync(scene, view_layer);
DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS);
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
}
}
static void hidebutton_layer_collection_flag_cb(bContext *C, void *poin, void *poin2)
{
wmWindow *win = CTX_wm_window(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = poin;
LayerCollection *lc = poin2;
bool extend = (CTX_wm_window(C)->eventstate->ctrl == 0);
Collection *collection = lc->collection;
bool do_disable = (win->eventstate->alt != 0);
bool do_isolate = (win->eventstate->ctrl != 0) && !do_disable;
bool extend = (win->eventstate->shift != 0);
bool depsgraph_changed = false;
/* Undo button toggle, let function do it. */
lc->flag ^= LAYER_COLLECTION_RESTRICT_VIEW;
if (do_disable) {
collection->flag |= COLLECTION_RESTRICT_VIEW;
depsgraph_changed = true;
}
else if (do_isolate) {
depsgraph_changed |= BKE_layer_collection_isolate(scene, view_layer, lc, extend);
}
else {
bool make_visible = ((lc->flag & LAYER_COLLECTION_RESTRICT_VIEW) != 0) ||
((collection->flag & COLLECTION_RESTRICT_VIEW) != 0);
depsgraph_changed |= BKE_layer_collection_set_visible(view_layer, lc, make_visible, extend);
}
BKE_layer_collection_set_visible(scene, view_layer, lc, extend);
DEG_relations_tag_update(CTX_data_main(C));
BKE_layer_collection_sync(scene, view_layer);
DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS);
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
if (depsgraph_changed) {
DEG_relations_tag_update(CTX_data_main(C));
}
WM_main_add_notifier(NC_SCENE | ND_LAYER_CONTENT, NULL);
}
static void namebutton_cb(bContext *C, void *tsep, char *oldname)
@ -517,27 +548,32 @@ static void outliner_draw_restrictbuts(
UI_but_drawflag_enable(bt, UI_BUT_ICON_REVERSE);
}
else if (tselem->type == 0 && te->idcode == ID_OB) {
PointerRNA ptr;
Object *ob = (Object *)tselem->id;
RNA_pointer_create(&ob->id, &RNA_Object, ob, &ptr);
Base *base = BKE_view_layer_base_find(view_layer, ob);
if (base) {
bt = uiDefIconButBitS(
block, UI_BTYPE_ICON_TOGGLE, BASE_HIDDEN, 0, ICON_HIDE_OFF,
(int)(ar->v2d.cur.xmax - OL_TOG_HIDEX), te->ys, UI_UNIT_X,
UI_UNIT_Y, &base->flag, 0, 0, 0, 0,
TIP_("Hide object in viewport (Ctrl to isolate)"));
int icon = ICON_RESTRICT_VIEW_ON;
if ((ob->restrictflag & OB_RESTRICT_VIEW) == 0) {
icon = (base->flag & BASE_HIDDEN) != 0 ?
ICON_HIDE_ON :
ICON_HIDE_OFF;
}
bt = uiDefIconBut(
block, UI_BTYPE_ICON_TOGGLE, 0, icon,
(int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), te->ys, UI_UNIT_X, UI_UNIT_Y,
NULL, 0, 0, 0, 0,
TIP_("Hide object in viewport (Alt to disable for all viewports)"));
UI_but_func_set(bt, hidebutton_base_flag_cb, view_layer, base);
UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
UI_but_drawflag_enable(bt, UI_BUT_ICON_REVERSE);
}
PointerRNA ptr;
RNA_pointer_create(&ob->id, &RNA_Object, ob, &ptr);
bt = uiDefIconButR_prop(block, UI_BTYPE_ICON_TOGGLE, 0, 0,
(int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), te->ys, UI_UNIT_X, UI_UNIT_Y,
&ptr, props.object_hide_viewport, -1, 0, 0, -1, -1, NULL);
UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
else {
bt = uiDefIconButR_prop(block, UI_BTYPE_ICON_TOGGLE, 0, 0,
(int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), te->ys, UI_UNIT_X, UI_UNIT_Y,
&ptr, props.object_hide_viewport, -1, 0, 0, -1, -1, NULL);
UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
}
bt = uiDefIconButR_prop(block, UI_BTYPE_ICON_TOGGLE, 0, 0,
(int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_SELECTX), te->ys, UI_UNIT_X, UI_UNIT_Y,
@ -641,23 +677,34 @@ static void outliner_draw_restrictbuts(
if ((!lc || !(lc->flag & LAYER_COLLECTION_EXCLUDE)) &&
!(collection->flag & COLLECTION_IS_MASTER))
{
bt = uiDefIconButBitS(
block, UI_BTYPE_ICON_TOGGLE, LAYER_COLLECTION_RESTRICT_VIEW, 0, ICON_HIDE_OFF,
(int)(ar->v2d.cur.xmax - OL_TOG_HIDEX), te->ys, UI_UNIT_X,
UI_UNIT_Y, &lc->flag, 0, 0, 0, 0,
TIP_("Hide collection in viewport (Ctrl to isolate)"));
UI_but_func_set(bt, hidebutton_layer_collection_flag_cb, view_layer, lc);
UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
UI_but_drawflag_enable(bt, UI_BUT_ICON_REVERSE);
PointerRNA collection_ptr;
RNA_id_pointer_create(&collection->id, &collection_ptr);
bt = uiDefIconButR_prop(
block, UI_BTYPE_ICON_TOGGLE, 0, 0,
(int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), te->ys, UI_UNIT_X,
UI_UNIT_Y, &collection_ptr, props.collection_hide_viewport, -1, 0, 0, 0, 0, NULL);
UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
if (lc != NULL) {
int icon = ICON_RESTRICT_VIEW_ON;
if ((collection->flag & COLLECTION_RESTRICT_VIEW) == 0) {
icon = (lc->flag & LAYER_COLLECTION_RESTRICT_VIEW) != 0 ?
ICON_HIDE_ON :
ICON_HIDE_OFF;
}
bt = uiDefIconBut(
block, UI_BTYPE_TOGGLE, 0, icon,
(int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), te->ys, UI_UNIT_X, UI_UNIT_Y,
NULL, 0, 0, 0, 0,
TIP_("Hide collection in viewport\n"
"* Alt to disable for all viewports\n"
"* Ctrl to isolate visibility\n"
"* Shift to hide inside objects and collections"));
UI_but_func_set(bt, hidebutton_layer_collection_flag_cb, view_layer, lc);
UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
}
else {
bt = uiDefIconButR_prop(
block, UI_BTYPE_ICON_TOGGLE, 0, 0,
(int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), te->ys, UI_UNIT_X,
UI_UNIT_Y, &collection_ptr, props.collection_hide_viewport, -1, 0, 0, 0, 0, NULL);
UI_but_flag_enable(bt, UI_BUT_DRAG_LOCK);
}
bt = uiDefIconButR_prop(
block, UI_BTYPE_ICON_TOGGLE, 0, 0,
@ -2115,10 +2162,7 @@ static void outliner_draw_restrictcols(ARegion *ar)
uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT);
immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
immUniformThemeColorShadeAlpha(TH_BACK, -15, -200);
immBegin(GPU_PRIM_LINES, 8);
immVertex2i(pos, (int)(ar->v2d.cur.xmax - OL_TOG_HIDEX), (int)ar->v2d.cur.ymax);
immVertex2i(pos, (int)(ar->v2d.cur.xmax - OL_TOG_HIDEX), (int)ar->v2d.cur.ymin);
immBegin(GPU_PRIM_LINES, 6);
immVertex2i(pos, (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), (int)ar->v2d.cur.ymax);
immVertex2i(pos, (int)(ar->v2d.cur.xmax - OL_TOG_RESTRICT_VIEWX), (int)ar->v2d.cur.ymin);

@ -123,12 +123,11 @@ typedef enum {
/* size constants */
#define OL_Y_OFFSET 2
#define OL_TOG_HIDEX (UI_UNIT_X * 4.0f + V2D_SCROLL_WIDTH)
#define OL_TOG_RESTRICT_SELECTX (UI_UNIT_X * 3.0f + V2D_SCROLL_WIDTH)
#define OL_TOG_RESTRICT_VIEWX (UI_UNIT_X * 2.0f + V2D_SCROLL_WIDTH)
#define OL_TOG_RESTRICT_RENDERX (UI_UNIT_X + V2D_SCROLL_WIDTH)
#define OL_TOGW OL_TOG_HIDEX
#define OL_TOGW OL_TOG_RESTRICT_SELECTX
#define OL_RNA_COLX (UI_UNIT_X * 15)
#define OL_RNA_COL_SIZEX (UI_UNIT_X * 7.5f)
@ -329,6 +328,16 @@ void OUTLINER_OT_collection_holdout_clear(struct wmOperatorType *ot);
void OUTLINER_OT_collection_indirect_only_set(struct wmOperatorType *ot);
void OUTLINER_OT_collection_indirect_only_clear(struct wmOperatorType *ot);
void OUTLINER_OT_collection_isolate(struct wmOperatorType *ot);
void OUTLINER_OT_collection_show(struct wmOperatorType *ot);
void OUTLINER_OT_collection_hide(struct wmOperatorType *ot);
void OUTLINER_OT_collection_show_inside(struct wmOperatorType *ot);
void OUTLINER_OT_collection_hide_inside(struct wmOperatorType *ot);
void OUTLINER_OT_collection_enable(struct wmOperatorType *ot);
void OUTLINER_OT_collection_disable(struct wmOperatorType *ot);
void OUTLINER_OT_collection_enable_render(struct wmOperatorType *ot);
void OUTLINER_OT_collection_disable_render(struct wmOperatorType *ot);
/* outliner_utils.c ---------------------------------------------- */
TreeElement *outliner_find_item_at_y(const SpaceOops *soops, const ListBase *tree, float view_co_y);

@ -105,6 +105,16 @@ void outliner_operatortypes(void)
WM_operatortype_append(OUTLINER_OT_collection_holdout_clear);
WM_operatortype_append(OUTLINER_OT_collection_indirect_only_set);
WM_operatortype_append(OUTLINER_OT_collection_indirect_only_clear);
WM_operatortype_append(OUTLINER_OT_collection_isolate);
WM_operatortype_append(OUTLINER_OT_collection_disable);
WM_operatortype_append(OUTLINER_OT_collection_enable);
WM_operatortype_append(OUTLINER_OT_collection_hide);
WM_operatortype_append(OUTLINER_OT_collection_show);
WM_operatortype_append(OUTLINER_OT_collection_disable_render);
WM_operatortype_append(OUTLINER_OT_collection_enable_render);
WM_operatortype_append(OUTLINER_OT_collection_hide_inside);
WM_operatortype_append(OUTLINER_OT_collection_show_inside);
}
void outliner_keymap(wmKeyConfig *keyconf)

@ -196,7 +196,7 @@ int rna_LayerCollection_name_length(PointerRNA *ptr)
return strlen(id->name + 2);
}
static void rna_LayerCollection_use_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
static void rna_LayerCollection_exclude_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
{
Scene *scene = (Scene *)ptr->id.data;
LayerCollection *lc = (LayerCollection *)ptr->data;
@ -209,6 +209,19 @@ static void rna_LayerCollection_use_update(Main *bmain, Scene *UNUSED(scene), Po
WM_main_add_notifier(NC_SCENE | ND_LAYER_CONTENT, NULL);
}
static void rna_LayerCollection_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
{
Scene *scene = (Scene *)ptr->id.data;
LayerCollection *lc = (LayerCollection *)ptr->data;
ViewLayer *view_layer = BKE_view_layer_find_from_collection(scene, lc);
BKE_layer_collection_sync(scene, view_layer);
DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS);
WM_main_add_notifier(NC_SCENE | ND_LAYER_CONTENT, NULL);
}
static bool rna_LayerCollection_has_objects(LayerCollection *lc)
{
return (lc->runtime_flag & LAYER_COLLECTION_HAS_OBJECTS) != 0;
@ -273,13 +286,13 @@ static void rna_def_layer_collection(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "flag", LAYER_COLLECTION_EXCLUDE);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_ui_text(prop, "Exclude", "Exclude collection from view layer");
RNA_def_property_update(prop, NC_SCENE | ND_LAYER, "rna_LayerCollection_use_update");
RNA_def_property_update(prop, NC_SCENE | ND_LAYER, "rna_LayerCollection_exclude_update");
prop = RNA_def_property(srna, "holdout", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", LAYER_COLLECTION_HOLDOUT);
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_ui_text(prop, "Holdout", "Mask out objects in collection from view layer");
RNA_def_property_update(prop, NC_SCENE | ND_LAYER, "rna_LayerCollection_use_update");
RNA_def_property_update(prop, NC_SCENE | ND_LAYER, "rna_LayerCollection_update");
prop = RNA_def_property(srna, "indirect_only", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", LAYER_COLLECTION_INDIRECT_ONLY);
@ -287,14 +300,14 @@ static void rna_def_layer_collection(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Indirect Only",
"Objects in collection only contribute indirectly (through shadows and reflections) "
"in the view layer");
RNA_def_property_update(prop, NC_SCENE | ND_LAYER, "rna_LayerCollection_use_update");
RNA_def_property_update(prop, NC_SCENE | ND_LAYER, "rna_LayerCollection_update");
prop = RNA_def_property(srna, "hide_viewport", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", LAYER_COLLECTION_RESTRICT_VIEW);
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_STATIC);
RNA_def_property_ui_icon(prop, ICON_HIDE_OFF, -1);
RNA_def_property_ui_text(prop, "Disable Viewport", "Disable collection in viewport for this view layer");
RNA_def_property_update(prop, NC_SCENE | ND_LAYER_CONTENT, "rna_LayerCollection_use_update");
RNA_def_property_update(prop, NC_SCENE | ND_LAYER_CONTENT, "rna_LayerCollection_update");
prop = RNA_def_property(srna, "is_visible", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "runtime_flag", LAYER_COLLECTION_VISIBLE);