From b6f44859d7ab5b128d71ce0d64b46b171b9357aa Mon Sep 17 00:00:00 2001 From: clayjohn Date: Wed, 12 Oct 2022 12:45:47 -0700 Subject: [PATCH] Implement multiple clip_children modes for CanvasItems --- doc/classes/CanvasItem.xml | 10 ++++++- doc/classes/RenderingServer.xml | 6 +++-- drivers/gles3/rasterizer_canvas_gles3.cpp | 12 ++++++--- scene/main/canvas_item.cpp | 26 ++++++++++++------- scene/main/canvas_item.h | 18 ++++++++++--- .../renderer_rd/effects/copy_effects.cpp | 4 +-- .../renderer_rd/renderer_canvas_render_rd.cpp | 16 +++++++++--- servers/rendering_server.cpp | 3 ++- servers/rendering_server.h | 3 ++- 9 files changed, 70 insertions(+), 28 deletions(-) diff --git a/doc/classes/CanvasItem.xml b/doc/classes/CanvasItem.xml index 947e6a3d4c0..b2d3e5cfdb6 100644 --- a/doc/classes/CanvasItem.xml +++ b/doc/classes/CanvasItem.xml @@ -535,7 +535,7 @@ - + Allows the current node to clip children nodes, essentially acting as a mask. @@ -653,5 +653,13 @@ Represents the size of the [enum TextureRepeat] enum. + + + + + + + + diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index 6aa92373855..376ba340562 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -4576,9 +4576,11 @@ - + - + + + diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp index 1648dddea18..c2c92055fd2 100644 --- a/drivers/gles3/rasterizer_canvas_gles3.cpp +++ b/drivers/gles3/rasterizer_canvas_gles3.cpp @@ -433,10 +433,12 @@ void RasterizerCanvasGLES3::canvas_render_items(RID p_to_render_target, Item *p_ _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, starting_index, false); item_count = 0; - if (ci->canvas_group_owner->canvas_group->mode == RS::CANVAS_GROUP_MODE_OPAQUE) { + if (ci->canvas_group_owner->canvas_group->mode != RS::CANVAS_GROUP_MODE_TRANSPARENT) { Rect2i group_rect = ci->canvas_group_owner->global_rect_cache; texture_storage->render_target_copy_to_back_buffer(p_to_render_target, group_rect, false); - items[item_count++] = ci->canvas_group_owner; + if (ci->canvas_group_owner->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_AND_DRAW) { + items[item_count++] = ci->canvas_group_owner; + } } else if (!backbuffer_cleared) { texture_storage->render_target_clear_back_buffer(p_to_render_target, Rect2i(), Color(0, 0, 0, 0)); backbuffer_cleared = true; @@ -545,12 +547,14 @@ void RasterizerCanvasGLES3::_render_items(RID p_to_render_target, int p_item_cou RID material = ci->material_owner == nullptr ? ci->material : ci->material_owner->material; if (ci->canvas_group != nullptr) { - if (ci->canvas_group->mode == RS::CANVAS_GROUP_MODE_OPAQUE) { + if (ci->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_AND_DRAW) { if (!p_to_backbuffer) { material = default_clip_children_material; } } else { - if (material.is_null()) { + if (ci->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_ONLY) { + material = default_clip_children_material; + } else { material = default_canvas_group_material; } } diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp index 05d86f77f25..fe680b56863 100644 --- a/scene/main/canvas_item.cpp +++ b/scene/main/canvas_item.cpp @@ -986,8 +986,8 @@ void CanvasItem::_bind_methods() { ClassDB::bind_method(D_METHOD("set_texture_repeat", "mode"), &CanvasItem::set_texture_repeat); ClassDB::bind_method(D_METHOD("get_texture_repeat"), &CanvasItem::get_texture_repeat); - ClassDB::bind_method(D_METHOD("set_clip_children", "enable"), &CanvasItem::set_clip_children); - ClassDB::bind_method(D_METHOD("is_clipping_children"), &CanvasItem::is_clipping_children); + ClassDB::bind_method(D_METHOD("set_clip_children_mode", "mode"), &CanvasItem::set_clip_children_mode); + ClassDB::bind_method(D_METHOD("get_clip_children_mode"), &CanvasItem::get_clip_children_mode); GDVIRTUAL_BIND(_draw); @@ -997,7 +997,7 @@ void CanvasItem::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::COLOR, "self_modulate"), "set_self_modulate", "get_self_modulate"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "show_behind_parent"), "set_draw_behind_parent", "is_draw_behind_parent_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "top_level"), "set_as_top_level", "is_set_as_top_level"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_children"), "set_clip_children", "is_clipping_children"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "clip_children", PROPERTY_HINT_ENUM, "Disabled,Clip Only, Clip + Draw"), "set_clip_children_mode", "get_clip_children_mode"); ADD_PROPERTY(PropertyInfo(Variant::INT, "light_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_light_mask", "get_light_mask"); ADD_GROUP("Texture", "texture_"); @@ -1035,6 +1035,11 @@ void CanvasItem::_bind_methods() { BIND_ENUM_CONSTANT(TEXTURE_REPEAT_ENABLED); BIND_ENUM_CONSTANT(TEXTURE_REPEAT_MIRROR); BIND_ENUM_CONSTANT(TEXTURE_REPEAT_MAX); + + BIND_ENUM_CONSTANT(CLIP_CHILDREN_DISABLED); + BIND_ENUM_CONSTANT(CLIP_CHILDREN_ONLY); + BIND_ENUM_CONSTANT(CLIP_CHILDREN_AND_DRAW); + BIND_ENUM_CONSTANT(CLIP_CHILDREN_MAX); } Transform2D CanvasItem::get_canvas_transform() const { @@ -1169,20 +1174,23 @@ void CanvasItem::set_texture_repeat(TextureRepeat p_texture_repeat) { notify_property_list_changed(); } -void CanvasItem::set_clip_children(bool p_enabled) { - if (clip_children == p_enabled) { +void CanvasItem::set_clip_children_mode(ClipChildrenMode p_clip_mode) { + ERR_FAIL_COND(p_clip_mode >= CLIP_CHILDREN_MAX); + + if (clip_children_mode == p_clip_mode) { return; } - clip_children = p_enabled; + clip_children_mode = p_clip_mode; if (Object::cast_to(this) != nullptr) { //avoid accidental bugs, make this not work on CanvasGroup return; } - RS::get_singleton()->canvas_item_set_canvas_group_mode(get_canvas_item(), clip_children ? RS::CANVAS_GROUP_MODE_OPAQUE : RS::CANVAS_GROUP_MODE_DISABLED); + + RS::get_singleton()->canvas_item_set_canvas_group_mode(get_canvas_item(), RS::CanvasGroupMode(clip_children_mode)); } -bool CanvasItem::is_clipping_children() const { - return clip_children; +CanvasItem::ClipChildrenMode CanvasItem::get_clip_children_mode() const { + return clip_children_mode; } CanvasItem::TextureRepeat CanvasItem::get_texture_repeat() const { diff --git a/scene/main/canvas_item.h b/scene/main/canvas_item.h index b80289fdb41..8e943be803f 100644 --- a/scene/main/canvas_item.h +++ b/scene/main/canvas_item.h @@ -66,8 +66,16 @@ public: TEXTURE_REPEAT_MAX, }; + enum ClipChildrenMode { + CLIP_CHILDREN_DISABLED, + CLIP_CHILDREN_ONLY, + CLIP_CHILDREN_AND_DRAW, + CLIP_CHILDREN_MAX, + }; + private: - mutable SelfList xform_change; + mutable SelfList + xform_change; RID canvas_item; StringName canvas_group; @@ -85,7 +93,6 @@ private: Window *window = nullptr; bool visible = true; bool parent_visible_in_tree = false; - bool clip_children = false; bool pending_update = false; bool top_level = false; bool drawing = false; @@ -95,6 +102,8 @@ private: bool notify_local_transform = false; bool notify_transform = false; + ClipChildrenMode clip_children_mode = CLIP_CHILDREN_DISABLED; + RS::CanvasItemTextureFilter texture_filter_cache = RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR; RS::CanvasItemTextureRepeat texture_repeat_cache = RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED; TextureFilter texture_filter = TEXTURE_FILTER_PARENT_NODE; @@ -200,8 +209,8 @@ public: void queue_redraw(); void move_to_front(); - void set_clip_children(bool p_enabled); - bool is_clipping_children() const; + void set_clip_children_mode(ClipChildrenMode p_clip_mode); + ClipChildrenMode get_clip_children_mode() const; virtual void set_light_mask(int p_light_mask); int get_light_mask() const; @@ -321,6 +330,7 @@ public: VARIANT_ENUM_CAST(CanvasItem::TextureFilter) VARIANT_ENUM_CAST(CanvasItem::TextureRepeat) +VARIANT_ENUM_CAST(CanvasItem::ClipChildrenMode) class CanvasTexture : public Texture2D { GDCLASS(CanvasTexture, Texture2D); diff --git a/servers/rendering/renderer_rd/effects/copy_effects.cpp b/servers/rendering/renderer_rd/effects/copy_effects.cpp index 53237c1dfb6..a14228eb3d6 100644 --- a/servers/rendering/renderer_rd/effects/copy_effects.cpp +++ b/servers/rendering/renderer_rd/effects/copy_effects.cpp @@ -357,8 +357,8 @@ void CopyEffects::copy_to_rect(RID p_source_rd_texture, RID p_dest_texture, cons copy.push_constant.flags |= COPY_FLAG_ALPHA_TO_ONE; } - copy.push_constant.section[0] = 0; - copy.push_constant.section[1] = 0; + copy.push_constant.section[0] = p_rect.position.x; + copy.push_constant.section[1] = p_rect.position.y; copy.push_constant.section[2] = p_rect.size.width; copy.push_constant.section[3] = p_rect.size.height; copy.push_constant.target[0] = p_rect.position.x; diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp index 7c4d5238a0f..7d524f949fb 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp @@ -1112,13 +1112,17 @@ void RendererCanvasRenderRD::_render_items(RID p_to_render_target, int p_item_co RID material = ci->material_owner == nullptr ? ci->material : ci->material_owner->material; if (ci->canvas_group != nullptr) { - if (ci->canvas_group->mode == RS::CANVAS_GROUP_MODE_OPAQUE) { + if (ci->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_AND_DRAW) { if (!p_to_backbuffer) { material = default_clip_children_material; } } else { if (material.is_null()) { - material = default_canvas_group_material; + if (ci->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_ONLY) { + material = default_clip_children_material; + } else { + material = default_canvas_group_material; + } } } } @@ -1445,10 +1449,12 @@ void RendererCanvasRenderRD::canvas_render_items(RID p_to_render_target, Item *p _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list); item_count = 0; - if (ci->canvas_group_owner->canvas_group->mode == RS::CANVAS_GROUP_MODE_OPAQUE) { + if (ci->canvas_group_owner->canvas_group->mode != RS::CANVAS_GROUP_MODE_TRANSPARENT) { Rect2i group_rect = ci->canvas_group_owner->global_rect_cache; texture_storage->render_target_copy_to_back_buffer(p_to_render_target, group_rect, false); - items[item_count++] = ci->canvas_group_owner; + if (ci->canvas_group_owner->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_AND_DRAW) { + items[item_count++] = ci->canvas_group_owner; + } } else if (!backbuffer_cleared) { texture_storage->render_target_clear_back_buffer(p_to_render_target, Rect2i(), Color(0, 0, 0, 0)); backbuffer_cleared = true; @@ -2752,7 +2758,9 @@ void fragment() { material_storage->shader_set_code(default_clip_children_shader, R"( // Default clip children shader. + shader_type canvas_item; + void fragment() { vec4 c = textureLod(SCREEN_TEXTURE, SCREEN_UV, 0.0); COLOR.rgb = c.rgb; diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index cf13118451c..e1a3fe46bec 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -2636,7 +2636,8 @@ void RenderingServer::_bind_methods() { BIND_ENUM_CONSTANT(CANVAS_ITEM_TEXTURE_REPEAT_MAX); BIND_ENUM_CONSTANT(CANVAS_GROUP_MODE_DISABLED); - BIND_ENUM_CONSTANT(CANVAS_GROUP_MODE_OPAQUE); + BIND_ENUM_CONSTANT(CANVAS_GROUP_MODE_CLIP_ONLY); + BIND_ENUM_CONSTANT(CANVAS_GROUP_MODE_CLIP_AND_DRAW); BIND_ENUM_CONSTANT(CANVAS_GROUP_MODE_TRANSPARENT); /* CANVAS LIGHT */ diff --git a/servers/rendering_server.h b/servers/rendering_server.h index 6885d80301a..3c6f46a985b 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -1367,7 +1367,8 @@ public: enum CanvasGroupMode { CANVAS_GROUP_MODE_DISABLED, - CANVAS_GROUP_MODE_OPAQUE, + CANVAS_GROUP_MODE_CLIP_ONLY, + CANVAS_GROUP_MODE_CLIP_AND_DRAW, CANVAS_GROUP_MODE_TRANSPARENT, };