From f4339d2a499c900be86a26f88be60c75ffe0e876 Mon Sep 17 00:00:00 2001 From: Relintai Date: Mon, 2 Oct 2023 17:32:07 +0200 Subject: [PATCH] Ported: CPUParticles2D - fix interpolated transforms and culling 1) Physics interpolated particles in global mode are specified in global space. In VisualServer they should therefore ignore local transform. 2) Additionally, the expected final_transform should be passed on to children, rather than the identity transform used on the local item. 3) Local bounds in hierarchical culling are fixed for items using identity transform, by calculating their local bound in local space from the global space particles. - lawnjelly https://github.com/godotengine/godot/commit/723632a76a6d88c2f95ef728bbf2f7182cab05a0 --- scene/2d/canvas_item.cpp | 2 +- servers/rendering/rasterizer.h | 4 +- servers/rendering/rendering_server_canvas.cpp | 84 ++++++++++++++++--- servers/rendering/rendering_server_canvas.h | 3 +- servers/rendering/rendering_server_raster.h | 2 +- servers/rendering/rendering_server_wrap_mt.h | 2 +- servers/rendering_server.h | 2 +- 7 files changed, 80 insertions(+), 19 deletions(-) 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,