Implement multiple clip_children modes for CanvasItems

This commit is contained in:
clayjohn 2022-10-12 12:45:47 -07:00
parent 09b1a6f85f
commit b6f44859d7
9 changed files with 70 additions and 28 deletions

View File

@ -535,7 +535,7 @@
</method> </method>
</methods> </methods>
<members> <members>
<member name="clip_children" type="bool" setter="set_clip_children" getter="is_clipping_children" default="false"> <member name="clip_children" type="int" setter="set_clip_children_mode" getter="get_clip_children_mode" enum="CanvasItem.ClipChildrenMode" default="0">
Allows the current node to clip children nodes, essentially acting as a mask. Allows the current node to clip children nodes, essentially acting as a mask.
</member> </member>
<member name="light_mask" type="int" setter="set_light_mask" getter="get_light_mask" default="1"> <member name="light_mask" type="int" setter="set_light_mask" getter="get_light_mask" default="1">
@ -653,5 +653,13 @@
<constant name="TEXTURE_REPEAT_MAX" value="4" enum="TextureRepeat"> <constant name="TEXTURE_REPEAT_MAX" value="4" enum="TextureRepeat">
Represents the size of the [enum TextureRepeat] enum. Represents the size of the [enum TextureRepeat] enum.
</constant> </constant>
<constant name="CLIP_CHILDREN_DISABLED" value="0" enum="ClipChildrenMode">
</constant>
<constant name="CLIP_CHILDREN_ONLY" value="1" enum="ClipChildrenMode">
</constant>
<constant name="CLIP_CHILDREN_AND_DRAW" value="2" enum="ClipChildrenMode">
</constant>
<constant name="CLIP_CHILDREN_MAX" value="3" enum="ClipChildrenMode">
</constant>
</constants> </constants>
</class> </class>

View File

@ -4576,9 +4576,11 @@
</constant> </constant>
<constant name="CANVAS_GROUP_MODE_DISABLED" value="0" enum="CanvasGroupMode"> <constant name="CANVAS_GROUP_MODE_DISABLED" value="0" enum="CanvasGroupMode">
</constant> </constant>
<constant name="CANVAS_GROUP_MODE_OPAQUE" value="1" enum="CanvasGroupMode"> <constant name="CANVAS_GROUP_MODE_CLIP_ONLY" value="1" enum="CanvasGroupMode">
</constant> </constant>
<constant name="CANVAS_GROUP_MODE_TRANSPARENT" value="2" enum="CanvasGroupMode"> <constant name="CANVAS_GROUP_MODE_CLIP_AND_DRAW" value="2" enum="CanvasGroupMode">
</constant>
<constant name="CANVAS_GROUP_MODE_TRANSPARENT" value="3" enum="CanvasGroupMode">
</constant> </constant>
<constant name="CANVAS_LIGHT_MODE_POINT" value="0" enum="CanvasLightMode"> <constant name="CANVAS_LIGHT_MODE_POINT" value="0" enum="CanvasLightMode">
</constant> </constant>

View File

@ -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); _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list, starting_index, false);
item_count = 0; 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; 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); texture_storage->render_target_copy_to_back_buffer(p_to_render_target, group_rect, false);
if (ci->canvas_group_owner->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_AND_DRAW) {
items[item_count++] = ci->canvas_group_owner; items[item_count++] = ci->canvas_group_owner;
}
} else if (!backbuffer_cleared) { } else if (!backbuffer_cleared) {
texture_storage->render_target_clear_back_buffer(p_to_render_target, Rect2i(), Color(0, 0, 0, 0)); texture_storage->render_target_clear_back_buffer(p_to_render_target, Rect2i(), Color(0, 0, 0, 0));
backbuffer_cleared = true; 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; RID material = ci->material_owner == nullptr ? ci->material : ci->material_owner->material;
if (ci->canvas_group != nullptr) { 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) { if (!p_to_backbuffer) {
material = default_clip_children_material; material = default_clip_children_material;
} }
} else { } 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; material = default_canvas_group_material;
} }
} }

View File

@ -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("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("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("set_clip_children_mode", "mode"), &CanvasItem::set_clip_children_mode);
ClassDB::bind_method(D_METHOD("is_clipping_children"), &CanvasItem::is_clipping_children); ClassDB::bind_method(D_METHOD("get_clip_children_mode"), &CanvasItem::get_clip_children_mode);
GDVIRTUAL_BIND(_draw); 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::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, "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, "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_PROPERTY(PropertyInfo(Variant::INT, "light_mask", PROPERTY_HINT_LAYERS_2D_RENDER), "set_light_mask", "get_light_mask");
ADD_GROUP("Texture", "texture_"); ADD_GROUP("Texture", "texture_");
@ -1035,6 +1035,11 @@ void CanvasItem::_bind_methods() {
BIND_ENUM_CONSTANT(TEXTURE_REPEAT_ENABLED); BIND_ENUM_CONSTANT(TEXTURE_REPEAT_ENABLED);
BIND_ENUM_CONSTANT(TEXTURE_REPEAT_MIRROR); BIND_ENUM_CONSTANT(TEXTURE_REPEAT_MIRROR);
BIND_ENUM_CONSTANT(TEXTURE_REPEAT_MAX); 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 { Transform2D CanvasItem::get_canvas_transform() const {
@ -1169,20 +1174,23 @@ void CanvasItem::set_texture_repeat(TextureRepeat p_texture_repeat) {
notify_property_list_changed(); notify_property_list_changed();
} }
void CanvasItem::set_clip_children(bool p_enabled) { void CanvasItem::set_clip_children_mode(ClipChildrenMode p_clip_mode) {
if (clip_children == p_enabled) { ERR_FAIL_COND(p_clip_mode >= CLIP_CHILDREN_MAX);
if (clip_children_mode == p_clip_mode) {
return; return;
} }
clip_children = p_enabled; clip_children_mode = p_clip_mode;
if (Object::cast_to<CanvasGroup>(this) != nullptr) { if (Object::cast_to<CanvasGroup>(this) != nullptr) {
//avoid accidental bugs, make this not work on CanvasGroup //avoid accidental bugs, make this not work on CanvasGroup
return; 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 { CanvasItem::ClipChildrenMode CanvasItem::get_clip_children_mode() const {
return clip_children; return clip_children_mode;
} }
CanvasItem::TextureRepeat CanvasItem::get_texture_repeat() const { CanvasItem::TextureRepeat CanvasItem::get_texture_repeat() const {

View File

@ -66,8 +66,16 @@ public:
TEXTURE_REPEAT_MAX, TEXTURE_REPEAT_MAX,
}; };
enum ClipChildrenMode {
CLIP_CHILDREN_DISABLED,
CLIP_CHILDREN_ONLY,
CLIP_CHILDREN_AND_DRAW,
CLIP_CHILDREN_MAX,
};
private: private:
mutable SelfList<Node> xform_change; mutable SelfList<Node>
xform_change;
RID canvas_item; RID canvas_item;
StringName canvas_group; StringName canvas_group;
@ -85,7 +93,6 @@ private:
Window *window = nullptr; Window *window = nullptr;
bool visible = true; bool visible = true;
bool parent_visible_in_tree = false; bool parent_visible_in_tree = false;
bool clip_children = false;
bool pending_update = false; bool pending_update = false;
bool top_level = false; bool top_level = false;
bool drawing = false; bool drawing = false;
@ -95,6 +102,8 @@ private:
bool notify_local_transform = false; bool notify_local_transform = false;
bool notify_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::CanvasItemTextureFilter texture_filter_cache = RS::CANVAS_ITEM_TEXTURE_FILTER_LINEAR;
RS::CanvasItemTextureRepeat texture_repeat_cache = RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED; RS::CanvasItemTextureRepeat texture_repeat_cache = RS::CANVAS_ITEM_TEXTURE_REPEAT_DISABLED;
TextureFilter texture_filter = TEXTURE_FILTER_PARENT_NODE; TextureFilter texture_filter = TEXTURE_FILTER_PARENT_NODE;
@ -200,8 +209,8 @@ public:
void queue_redraw(); void queue_redraw();
void move_to_front(); void move_to_front();
void set_clip_children(bool p_enabled); void set_clip_children_mode(ClipChildrenMode p_clip_mode);
bool is_clipping_children() const; ClipChildrenMode get_clip_children_mode() const;
virtual void set_light_mask(int p_light_mask); virtual void set_light_mask(int p_light_mask);
int get_light_mask() const; int get_light_mask() const;
@ -321,6 +330,7 @@ public:
VARIANT_ENUM_CAST(CanvasItem::TextureFilter) VARIANT_ENUM_CAST(CanvasItem::TextureFilter)
VARIANT_ENUM_CAST(CanvasItem::TextureRepeat) VARIANT_ENUM_CAST(CanvasItem::TextureRepeat)
VARIANT_ENUM_CAST(CanvasItem::ClipChildrenMode)
class CanvasTexture : public Texture2D { class CanvasTexture : public Texture2D {
GDCLASS(CanvasTexture, Texture2D); GDCLASS(CanvasTexture, Texture2D);

View File

@ -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.flags |= COPY_FLAG_ALPHA_TO_ONE;
} }
copy.push_constant.section[0] = 0; copy.push_constant.section[0] = p_rect.position.x;
copy.push_constant.section[1] = 0; copy.push_constant.section[1] = p_rect.position.y;
copy.push_constant.section[2] = p_rect.size.width; copy.push_constant.section[2] = p_rect.size.width;
copy.push_constant.section[3] = p_rect.size.height; copy.push_constant.section[3] = p_rect.size.height;
copy.push_constant.target[0] = p_rect.position.x; copy.push_constant.target[0] = p_rect.position.x;

View File

@ -1112,16 +1112,20 @@ 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; RID material = ci->material_owner == nullptr ? ci->material : ci->material_owner->material;
if (ci->canvas_group != nullptr) { 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) { if (!p_to_backbuffer) {
material = default_clip_children_material; material = default_clip_children_material;
} }
} else { } else {
if (material.is_null()) { 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; material = default_canvas_group_material;
} }
} }
} }
}
if (material != prev_material) { if (material != prev_material) {
CanvasMaterialData *material_data = nullptr; CanvasMaterialData *material_data = nullptr;
@ -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); _render_items(p_to_render_target, item_count, canvas_transform_inverse, p_light_list);
item_count = 0; 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; 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); texture_storage->render_target_copy_to_back_buffer(p_to_render_target, group_rect, false);
if (ci->canvas_group_owner->canvas_group->mode == RS::CANVAS_GROUP_MODE_CLIP_AND_DRAW) {
items[item_count++] = ci->canvas_group_owner; items[item_count++] = ci->canvas_group_owner;
}
} else if (!backbuffer_cleared) { } else if (!backbuffer_cleared) {
texture_storage->render_target_clear_back_buffer(p_to_render_target, Rect2i(), Color(0, 0, 0, 0)); texture_storage->render_target_clear_back_buffer(p_to_render_target, Rect2i(), Color(0, 0, 0, 0));
backbuffer_cleared = true; backbuffer_cleared = true;
@ -2752,7 +2758,9 @@ void fragment() {
material_storage->shader_set_code(default_clip_children_shader, R"( material_storage->shader_set_code(default_clip_children_shader, R"(
// Default clip children shader. // Default clip children shader.
shader_type canvas_item; shader_type canvas_item;
void fragment() { void fragment() {
vec4 c = textureLod(SCREEN_TEXTURE, SCREEN_UV, 0.0); vec4 c = textureLod(SCREEN_TEXTURE, SCREEN_UV, 0.0);
COLOR.rgb = c.rgb; COLOR.rgb = c.rgb;

View File

@ -2636,7 +2636,8 @@ void RenderingServer::_bind_methods() {
BIND_ENUM_CONSTANT(CANVAS_ITEM_TEXTURE_REPEAT_MAX); BIND_ENUM_CONSTANT(CANVAS_ITEM_TEXTURE_REPEAT_MAX);
BIND_ENUM_CONSTANT(CANVAS_GROUP_MODE_DISABLED); 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); BIND_ENUM_CONSTANT(CANVAS_GROUP_MODE_TRANSPARENT);
/* CANVAS LIGHT */ /* CANVAS LIGHT */

View File

@ -1367,7 +1367,8 @@ public:
enum CanvasGroupMode { enum CanvasGroupMode {
CANVAS_GROUP_MODE_DISABLED, CANVAS_GROUP_MODE_DISABLED,
CANVAS_GROUP_MODE_OPAQUE, CANVAS_GROUP_MODE_CLIP_ONLY,
CANVAS_GROUP_MODE_CLIP_AND_DRAW,
CANVAS_GROUP_MODE_TRANSPARENT, CANVAS_GROUP_MODE_TRANSPARENT,
}; };