Merge pull request #80887 from lawnjelly/cpuparticles2d_fix_double_xform
[3.x] CPUParticles2D - fix interpolated transforms and culling
This commit is contained in:
commit
cc48c1825f
@ -732,7 +732,7 @@ void CanvasItem::set_canvas_item_use_identity_transform(bool p_enable) {
|
|||||||
_set_use_identity_transform(p_enable);
|
_set_use_identity_transform(p_enable);
|
||||||
|
|
||||||
// Let VisualServer know not to concatenate the parent transform during the render.
|
// 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 (is_inside_tree()) {
|
||||||
if (p_enable) {
|
if (p_enable) {
|
||||||
|
@ -1006,7 +1006,7 @@ public:
|
|||||||
bool light_masked : 1;
|
bool light_masked : 1;
|
||||||
bool on_interpolate_transform_list : 1;
|
bool on_interpolate_transform_list : 1;
|
||||||
bool interpolated : 1;
|
bool interpolated : 1;
|
||||||
bool ignore_parent_xform : 1;
|
bool use_identity_xform : 1;
|
||||||
mutable bool custom_rect : 1;
|
mutable bool custom_rect : 1;
|
||||||
mutable bool rect_dirty : 1;
|
mutable bool rect_dirty : 1;
|
||||||
mutable bool bound_dirty : 1;
|
mutable bool bound_dirty : 1;
|
||||||
@ -1262,7 +1262,7 @@ public:
|
|||||||
update_when_visible = false;
|
update_when_visible = false;
|
||||||
on_interpolate_transform_list = false;
|
on_interpolate_transform_list = false;
|
||||||
interpolated = true;
|
interpolated = true;
|
||||||
ignore_parent_xform = false;
|
use_identity_xform = false;
|
||||||
local_bound_last_update_tick = 0;
|
local_bound_last_update_tick = 0;
|
||||||
}
|
}
|
||||||
virtual ~Item() {
|
virtual ~Item() {
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
/**************************************************************************/
|
/**************************************************************************/
|
||||||
|
|
||||||
#include "visual_server_canvas.h"
|
#include "visual_server_canvas.h"
|
||||||
|
#include "core/fixed_array.h"
|
||||||
#include "core/math/transform_interpolator.h"
|
#include "core/math/transform_interpolator.h"
|
||||||
#include "visual_server_globals.h"
|
#include "visual_server_globals.h"
|
||||||
#include "visual_server_raster.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<const Transform2D *, 64> 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) {
|
void VisualServerCanvas::_finalize_and_merge_local_bound_to_branch(Item *p_canvas_item, Rect2 *r_branch_bound) {
|
||||||
if (r_branch_bound) {
|
if (r_branch_bound) {
|
||||||
Rect2 this_rect = p_canvas_item->get_rect();
|
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 this item has a bound...
|
||||||
if (!p_canvas_item->local_bound.has_no_area()) {
|
if (!p_canvas_item->local_bound.has_no_area()) {
|
||||||
// If the rect has an 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);
|
TransformInterpolator::interpolate_transform_2d(ci->xform_prev, ci->xform_curr, final_xform, f);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!p_canvas_item->ignore_parent_xform) {
|
// Always calculate final transform as if not using identity xform.
|
||||||
final_xform = p_transform * final_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 {
|
} 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;
|
global_rect.position += p_clip_rect.position;
|
||||||
|
|
||||||
if (ci->use_parent_material && p_material_owner) {
|
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) {
|
if ((!ci->commands.empty() && p_clip_rect.intersects(global_rect, true)) || ci->vp_render || ci->copy_back_buffer) {
|
||||||
//something to draw?
|
//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->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 = global_rect;
|
||||||
ci->global_rect_cache.position -= p_clip_rect.position;
|
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);
|
TransformInterpolator::interpolate_transform_2d(ci->xform_prev, ci->xform_curr, final_xform, f);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!p_canvas_item->ignore_parent_xform) {
|
// Always calculate final transform as if not using identity xform.
|
||||||
final_xform = p_transform * final_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 {
|
} 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->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;
|
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);
|
_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);
|
Item *canvas_item = canvas_item_owner.getornull(p_item);
|
||||||
ERR_FAIL_COND(!canvas_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);
|
_make_bound_dirty(canvas_item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,6 +181,7 @@ private:
|
|||||||
void _prepare_tree_bounds(Item *p_root);
|
void _prepare_tree_bounds(Item *p_root);
|
||||||
void _calculate_canvas_item_bound(Item *p_canvas_item, Rect2 *r_branch_bound);
|
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 _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);
|
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_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_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);
|
void canvas_item_set_update_when_visible(RID p_item, bool p_update);
|
||||||
|
|
||||||
|
@ -690,7 +690,7 @@ public:
|
|||||||
BIND2(canvas_item_set_self_modulate, RID, const Color &)
|
BIND2(canvas_item_set_self_modulate, RID, const Color &)
|
||||||
|
|
||||||
BIND2(canvas_item_set_draw_behind_parent, RID, bool)
|
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)
|
BIND6(canvas_item_add_line, RID, const Point2 &, const Point2 &, const Color &, float, bool)
|
||||||
BIND5(canvas_item_add_polyline, RID, const Vector<Point2> &, const Vector<Color> &, float, bool)
|
BIND5(canvas_item_add_polyline, RID, const Vector<Point2> &, const Vector<Color> &, float, bool)
|
||||||
|
@ -594,7 +594,7 @@ public:
|
|||||||
FUNC2(canvas_item_set_self_modulate, RID, const Color &)
|
FUNC2(canvas_item_set_self_modulate, RID, const Color &)
|
||||||
|
|
||||||
FUNC2(canvas_item_set_draw_behind_parent, RID, bool)
|
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)
|
FUNC6(canvas_item_add_line, RID, const Point2 &, const Point2 &, const Color &, float, bool)
|
||||||
FUNC5(canvas_item_add_polyline, RID, const Vector<Point2> &, const Vector<Color> &, float, bool)
|
FUNC5(canvas_item_add_polyline, RID, const Vector<Point2> &, const Vector<Color> &, float, bool)
|
||||||
|
@ -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_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_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 {
|
enum NinePatchAxisMode {
|
||||||
NINE_PATCH_STRETCH,
|
NINE_PATCH_STRETCH,
|
||||||
|
Loading…
Reference in New Issue
Block a user