diff --git a/scene/2d/canvas_item.cpp b/scene/2d/canvas_item.cpp index 6d126fa599c..70022a36601 100644 --- a/scene/2d/canvas_item.cpp +++ b/scene/2d/canvas_item.cpp @@ -732,7 +732,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. - VisualServer::get_singleton()->canvas_item_set_ignore_parent_transform(get_canvas_item(), p_enable); + VisualServer::get_singleton()->canvas_item_set_use_identity_transform(get_canvas_item(), p_enable); if (is_inside_tree()) { if (p_enable) { diff --git a/servers/visual/rasterizer.h b/servers/visual/rasterizer.h index 74833ddf36b..17dee11b288 100644 --- a/servers/visual/rasterizer.h +++ b/servers/visual/rasterizer.h @@ -1006,7 +1006,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; @@ -1262,7 +1262,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; } virtual ~Item() { diff --git a/servers/visual/visual_server_canvas.cpp b/servers/visual/visual_server_canvas.cpp index 47441dd52be..0bede7b1841 100644 --- a/servers/visual/visual_server_canvas.cpp +++ b/servers/visual/visual_server_canvas.cpp @@ -29,6 +29,7 @@ /**************************************************************************/ #include "visual_server_canvas.h" +#include "core/fixed_array.h" #include "core/math/transform_interpolator.h" #include "visual_server_globals.h" #include "visual_server_raster.h" @@ -277,10 +278,57 @@ void VisualServerCanvas::_calculate_canvas_item_bound(Item *p_canvas_item, Rect2 } } +Transform2D VisualServerCanvas::_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 VisualServerCanvas::_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... @@ -340,13 +388,19 @@ void VisualServerCanvas::_render_canvas_item_cull_by_item(Item *p_canvas_item, c 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) { @@ -422,7 +476,7 @@ void VisualServerCanvas::_render_canvas_item_cull_by_item(Item *p_canvas_item, c 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; @@ -481,15 +535,21 @@ void VisualServerCanvas::_render_canvas_item_cull_by_node(Item *p_canvas_item, c 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; @@ -955,11 +1015,11 @@ void VisualServerCanvas::canvas_item_set_draw_behind_parent(RID p_item, bool p_e _check_bound_integrity(canvas_item); } -void VisualServerCanvas::canvas_item_set_ignore_parent_transform(RID p_item, bool p_enable) { +void VisualServerCanvas::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/visual/visual_server_canvas.h b/servers/visual/visual_server_canvas.h index fad0e10f60a..0f5c15c7f81 100644 --- a/servers/visual/visual_server_canvas.h +++ b/servers/visual/visual_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/visual/visual_server_raster.h b/servers/visual/visual_server_raster.h index 26362642f51..17155083c45 100644 --- a/servers/visual/visual_server_raster.h +++ b/servers/visual/visual_server_raster.h @@ -690,7 +690,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/visual/visual_server_wrap_mt.h b/servers/visual/visual_server_wrap_mt.h index 36219560453..7b459bfc30f 100644 --- a/servers/visual/visual_server_wrap_mt.h +++ b/servers/visual/visual_server_wrap_mt.h @@ -594,7 +594,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/visual_server.h b/servers/visual_server.h index a67f5fa57fc..f78ec65f44f 100644 --- a/servers/visual_server.h +++ b/servers/visual_server.h @@ -1024,7 +1024,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,