diff --git a/scene/2d/canvas_item.cpp b/scene/2d/canvas_item.cpp index b21809441..e72c6bd78 100644 --- a/scene/2d/canvas_item.cpp +++ b/scene/2d/canvas_item.cpp @@ -747,7 +747,7 @@ void CanvasItem::set_canvas_item_use_identity_transform(bool p_enable) { _set_use_identity_transform(p_enable); // Let VisualServer know not to concatenate the parent transform during the render. - RenderingServer::get_singleton()->canvas_item_set_ignore_parent_transform(get_canvas_item(), p_enable); + RenderingServer::get_singleton()->canvas_item_set_use_identity_transform(get_canvas_item(), p_enable); if (is_inside_tree()) { if (p_enable) { diff --git a/servers/rendering/rasterizer.h b/servers/rendering/rasterizer.h index 5a2bff2af..6aa1d76ab 100644 --- a/servers/rendering/rasterizer.h +++ b/servers/rendering/rasterizer.h @@ -868,7 +868,7 @@ public: bool light_masked : 1; bool on_interpolate_transform_list : 1; bool interpolated : 1; - bool ignore_parent_xform : 1; + bool use_identity_xform : 1; mutable bool custom_rect : 1; mutable bool rect_dirty : 1; mutable bool bound_dirty : 1; @@ -1120,7 +1120,7 @@ public: update_when_visible = false; on_interpolate_transform_list = false; interpolated = true; - ignore_parent_xform = false; + use_identity_xform = false; local_bound_last_update_tick = 0; } diff --git a/servers/rendering/rendering_server_canvas.cpp b/servers/rendering/rendering_server_canvas.cpp index 3c2a8d32d..11d95e172 100644 --- a/servers/rendering/rendering_server_canvas.cpp +++ b/servers/rendering/rendering_server_canvas.cpp @@ -30,6 +30,7 @@ #include "core/config/project_settings.h" +#include "core/containers/fixed_array.h" #include "core/math/transform_interpolator.h" #include "rendering_server_canvas.h" @@ -280,10 +281,57 @@ void RenderingServerCanvas::_calculate_canvas_item_bound(Item *p_canvas_item, Re } } +Transform2D RenderingServerCanvas::_calculate_item_global_xform(const Item *p_canvas_item) { + // If we use more than the maximum scene tree depth, we are out of luck. + // But that would be super inefficient anyway. + FixedArray transforms; + + while (p_canvas_item) { + // Should only happen if scene tree depth too high. + if (transforms.is_full()) { + WARN_PRINT_ONCE("SceneTree depth too high for hierarchical culling."); + break; + } + + // Note this is only using the CURRENT transform. + // This may have implications for interpolated bounds - investigate. + transforms.push_back(&p_canvas_item->xform_curr); + + if (canvas_item_owner.owns(p_canvas_item->parent)) { + p_canvas_item = canvas_item_owner.get(p_canvas_item->parent); + } else { + p_canvas_item = nullptr; + } + } + + Transform2D tr; + for (int n = (int)transforms.size() - 1; n >= 0; n--) { + tr *= *transforms[n]; + } + return tr; +} + void RenderingServerCanvas::_finalize_and_merge_local_bound_to_branch(Item *p_canvas_item, Rect2 *r_branch_bound) { if (r_branch_bound) { Rect2 this_rect = p_canvas_item->get_rect(); + // Special case .. if the canvas_item has use_identity_xform, + // we need to transform the rect from global space to local space, + // because the hierarchical culling expects local space. + if (p_canvas_item->use_identity_xform) { + // This is incredibly inefficient, but should only occur for e.g. CPUParticles2D, + // and is difficult to avoid because global transform is not usually kept track of + // in VisualServer (only final transform which is combinated with camera, and that + // is only calculated on render, so is no use for culling purposes). + Transform2D global_xform = _calculate_item_global_xform(p_canvas_item); + this_rect = global_xform.affine_inverse().xform(this_rect); + + // Note that the efficiency will depend linearly on the scene tree depth of the + // identity transform item. + // So e.g. interpolated global CPUParticles2D may run faster at lower depths + // in extreme circumstances. + } + // If this item has a bound... if (!p_canvas_item->local_bound.has_no_area()) { // If the rect has an area... @@ -342,13 +390,19 @@ void RenderingServerCanvas::_render_canvas_item_cull_by_item(Item *p_canvas_item TransformInterpolator::interpolate_transform_2d(ci->xform_prev, ci->xform_curr, final_xform, f); } - if (!p_canvas_item->ignore_parent_xform) { - final_xform = p_transform * final_xform; + // Always calculate final transform as if not using identity xform. + // This is so the expected transform is passed to children. + // However, if use_identity_xform is set, + // we can override the transform for rendering purposes for this item only. + final_xform = p_transform * final_xform; + + Rect2 global_rect; + if (!p_canvas_item->use_identity_xform) { + global_rect = final_xform.xform(rect); } else { - final_xform = _current_camera_transform * final_xform; + global_rect = _current_camera_transform.xform(rect); } - Rect2 global_rect = final_xform.xform(rect); global_rect.position += p_clip_rect.position; if (ci->use_parent_material && p_material_owner) { @@ -424,7 +478,7 @@ void RenderingServerCanvas::_render_canvas_item_cull_by_item(Item *p_canvas_item if ((!ci->commands.empty() && p_clip_rect.intersects(global_rect, true)) || ci->vp_render || ci->copy_back_buffer) { //something to draw? - ci->final_transform = final_xform; + ci->final_transform = !p_canvas_item->use_identity_xform ? final_xform : _current_camera_transform; ci->final_modulate = Color(modulate.r * ci->self_modulate.r, modulate.g * ci->self_modulate.g, modulate.b * ci->self_modulate.b, modulate.a * ci->self_modulate.a); ci->global_rect_cache = global_rect; ci->global_rect_cache.position -= p_clip_rect.position; @@ -483,15 +537,21 @@ void RenderingServerCanvas::_render_canvas_item_cull_by_node(Item *p_canvas_item TransformInterpolator::interpolate_transform_2d(ci->xform_prev, ci->xform_curr, final_xform, f); } - if (!p_canvas_item->ignore_parent_xform) { - final_xform = p_transform * final_xform; + // Always calculate final transform as if not using identity xform. + // This is so the expected transform is passed to children. + // However, if use_identity_xform is set, + // we can override the transform for rendering purposes for this item only. + final_xform = p_transform * final_xform; + + Rect2 global_rect; + if (!p_canvas_item->use_identity_xform) { + global_rect = final_xform.xform(rect); } else { - final_xform = _current_camera_transform * final_xform; + global_rect = _current_camera_transform.xform(rect); } - Rect2 global_rect = final_xform.xform(rect); ci->global_rect_cache = global_rect; - ci->final_transform = final_xform; + ci->final_transform = !p_canvas_item->use_identity_xform ? final_xform : _current_camera_transform; global_rect.position += p_clip_rect.position; @@ -967,11 +1027,11 @@ void RenderingServerCanvas::canvas_item_set_draw_behind_parent(RID p_item, bool _make_bound_dirty(canvas_item); } -void RenderingServerCanvas::canvas_item_set_ignore_parent_transform(RID p_item, bool p_enable) { +void RenderingServerCanvas::canvas_item_set_use_identity_transform(RID p_item, bool p_enable) { Item *canvas_item = canvas_item_owner.getornull(p_item); ERR_FAIL_COND(!canvas_item); - canvas_item->ignore_parent_xform = p_enable; + canvas_item->use_identity_xform = p_enable; _make_bound_dirty(canvas_item); } diff --git a/servers/rendering/rendering_server_canvas.h b/servers/rendering/rendering_server_canvas.h index d1b3c675d..646c1dbe6 100644 --- a/servers/rendering/rendering_server_canvas.h +++ b/servers/rendering/rendering_server_canvas.h @@ -181,6 +181,7 @@ private: void _prepare_tree_bounds(Item *p_root); void _calculate_canvas_item_bound(Item *p_canvas_item, Rect2 *r_branch_bound); + Transform2D _calculate_item_global_xform(const Item *p_canvas_item); void _finalize_and_merge_local_bound_to_branch(Item *p_canvas_item, Rect2 *r_branch_bound); void _merge_local_bound_to_branch(Item *p_canvas_item, Rect2 *r_branch_bound); @@ -227,7 +228,7 @@ public: void canvas_item_set_self_modulate(RID p_item, const Color &p_color); void canvas_item_set_draw_behind_parent(RID p_item, bool p_enable); - void canvas_item_set_ignore_parent_transform(RID p_item, bool p_enable); + void canvas_item_set_use_identity_transform(RID p_item, bool p_enable); void canvas_item_set_update_when_visible(RID p_item, bool p_update); diff --git a/servers/rendering/rendering_server_raster.h b/servers/rendering/rendering_server_raster.h index 1840428f9..3a9f2a10b 100644 --- a/servers/rendering/rendering_server_raster.h +++ b/servers/rendering/rendering_server_raster.h @@ -594,7 +594,7 @@ public: BIND2(canvas_item_set_self_modulate, RID, const Color &) BIND2(canvas_item_set_draw_behind_parent, RID, bool) - BIND2(canvas_item_set_ignore_parent_transform, RID, bool) + BIND2(canvas_item_set_use_identity_transform, RID, bool) BIND6(canvas_item_add_line, RID, const Point2 &, const Point2 &, const Color &, float, bool) BIND5(canvas_item_add_polyline, RID, const Vector &, const Vector &, float, bool) diff --git a/servers/rendering/rendering_server_wrap_mt.h b/servers/rendering/rendering_server_wrap_mt.h index 93301c89f..c53278503 100644 --- a/servers/rendering/rendering_server_wrap_mt.h +++ b/servers/rendering/rendering_server_wrap_mt.h @@ -502,7 +502,7 @@ public: FUNC2(canvas_item_set_self_modulate, RID, const Color &) FUNC2(canvas_item_set_draw_behind_parent, RID, bool) - FUNC2(canvas_item_set_ignore_parent_transform, RID, bool) + FUNC2(canvas_item_set_use_identity_transform, RID, bool) FUNC6(canvas_item_add_line, RID, const Point2 &, const Point2 &, const Color &, float, bool) FUNC5(canvas_item_add_polyline, RID, const Vector &, const Vector &, float, bool) diff --git a/servers/rendering_server.h b/servers/rendering_server.h index a098ac79b..bf594a813 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -915,7 +915,7 @@ public: virtual void canvas_item_set_self_modulate(RID p_item, const Color &p_color) = 0; virtual void canvas_item_set_draw_behind_parent(RID p_item, bool p_enable) = 0; - virtual void canvas_item_set_ignore_parent_transform(RID p_item, bool p_enable) = 0; + virtual void canvas_item_set_use_identity_transform(RID p_item, bool p_enable) = 0; enum NinePatchAxisMode { NINE_PATCH_STRETCH,