Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions core/math/geometry_2d.h
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,38 @@ class Geometry2D {
return (real_t)(A.x - O.x) * (B.y - O.y) - (real_t)(A.y - O.y) * (B.x - O.x);
}

static Vector<Point2> get_rect_outline(const Rect2 &p_rect) {
Vector<Vector2> points;
points.resize(5);
points.write[0] = p_rect.position;
points.write[1] = p_rect.position + Vector2(p_rect.size.x, 0);
points.write[2] = p_rect.position + p_rect.size;
points.write[3] = p_rect.position + Vector2(0, p_rect.size.y);
points.write[4] = p_rect.position;
return points;
}

static Vector<Point2> get_ellipse_outline(const Point2 &p_point, real_t p_major, real_t p_minor) {
// Tessellation count is hardcoded. Keep in sync with the same variable in `RendererCanvasCull::canvas_item_add_circle()`.
const int circle_segments = 64;

Vector<Vector2> points;
points.resize(circle_segments + 1);

Vector2 *points_ptr = points.ptrw();
const real_t circle_point_step = Math::TAU / circle_segments;

for (int i = 0; i < circle_segments; i++) {
float angle = i * circle_point_step;
points_ptr[i].x = Math::cos(angle) * p_major;
points_ptr[i].y = Math::sin(angle) * p_minor;
points_ptr[i] += p_point;
}
points_ptr[circle_segments] = points_ptr[0];

return points;
}

// Returns a list of points on the convex hull in counter-clockwise order.
// Note: the last point in the returned list is the same as the first one.
static Vector<Point2> convex_hull(Vector<Point2> P) {
Expand Down
35 changes: 24 additions & 11 deletions scene/2d/cpu_particles_2d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "cpu_particles_2d.h"
#include "cpu_particles_2d.compat.inc"

#include "core/math/geometry_2d.h"
#include "core/math/random_number_generator.h"
#include "core/math/transform_interpolator.h"
#include "scene/2d/gpu_particles_2d.h"
Expand Down Expand Up @@ -602,6 +603,9 @@ void CPUParticles2D::set_show_gizmos(bool p_show_gizmos) {
return;
}
show_gizmos = p_show_gizmos;
if (!show_gizmos) {
RS::get_singleton()->canvas_item_clear(gizmo_canvas_item);
}
queue_redraw();
}
#endif
Expand Down Expand Up @@ -1277,6 +1281,7 @@ void CPUParticles2D::_notification(int p_what) {

#ifdef TOOLS_ENABLED
if (show_gizmos) {
RS::get_singleton()->canvas_item_clear(gizmo_canvas_item);
_draw_emission_gizmo();
}
#endif
Expand Down Expand Up @@ -1320,23 +1325,22 @@ void CPUParticles2D::_notification(int p_what) {
#ifdef TOOLS_ENABLED
void CPUParticles2D::_draw_emission_gizmo() {
Color emission_ring_color = Color(0.8, 0.7, 0.4, 0.4);
Transform2D gizmo_transform;
if (!local_coords) {
gizmo_transform = get_global_transform();
}

draw_set_transform_matrix(gizmo_transform);

switch (emission_shape) {
case CPUParticles2D::EMISSION_SHAPE_RECTANGLE:
draw_rect(Rect2(-emission_rect_extents, emission_rect_extents * 2.0), emission_ring_color, false);
case CPUParticles2D::EMISSION_SHAPE_RECTANGLE: {
Vector<Vector2> pos = Geometry2D::get_rect_outline(Rect2(-emission_rect_extents, emission_rect_extents * 2.0));
RS::get_singleton()->canvas_item_add_polyline(gizmo_canvas_item, pos, { emission_ring_color });
break;
}
case CPUParticles2D::EMISSION_SHAPE_SPHERE:
case CPUParticles2D::EMISSION_SHAPE_SPHERE_SURFACE:
draw_circle(Vector2(), emission_sphere_radius, emission_ring_color, false);
case CPUParticles2D::EMISSION_SHAPE_SPHERE_SURFACE: {
Vector<Vector2> pos = Geometry2D::get_ellipse_outline(Vector2(), emission_sphere_radius, emission_sphere_radius);
RS::get_singleton()->canvas_item_add_polyline(gizmo_canvas_item, pos, { emission_ring_color });
break;
default:
}
default: {
break;
}
}
}
#endif
Expand Down Expand Up @@ -1671,6 +1675,12 @@ CPUParticles2D::CPUParticles2D() {
multimesh = RenderingServer::get_singleton()->multimesh_create();
RenderingServer::get_singleton()->multimesh_set_mesh(multimesh, mesh);

#ifdef TOOLS_ENABLED
gizmo_canvas_item = RS::get_singleton()->canvas_item_create();
RS::get_singleton()->canvas_item_set_z_index(gizmo_canvas_item, RS::CANVAS_ITEM_Z_MAX - 1);
RS::get_singleton()->canvas_item_set_parent(gizmo_canvas_item, get_canvas_item());
#endif

set_emitting(true);
set_amount(8);
set_use_local_coordinates(false);
Expand Down Expand Up @@ -1723,4 +1733,7 @@ CPUParticles2D::~CPUParticles2D() {
ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free_rid(multimesh);
RS::get_singleton()->free_rid(mesh);
#ifdef TOOLS_ENABLED
RS::get_singleton()->free_rid(gizmo_canvas_item);
#endif
}
1 change: 1 addition & 0 deletions scene/2d/cpu_particles_2d.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ class CPUParticles2D : public Node2D {
Transform2D inv_emission_transform;

#ifdef TOOLS_ENABLED
RID gizmo_canvas_item;
bool show_gizmos = false;
#endif

Expand Down
43 changes: 33 additions & 10 deletions scene/2d/gpu_particles_2d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "gpu_particles_2d.h"
#include "gpu_particles_2d.compat.inc"

#include "core/math/geometry_2d.h"
#include "scene/2d/cpu_particles_2d.h"
#include "scene/resources/atlas_texture.h"
#include "scene/resources/canvas_item_material.h"
Expand Down Expand Up @@ -218,6 +219,9 @@ void GPUParticles2D::set_show_gizmos(bool p_show_gizmos) {
return;
}
show_gizmos = p_show_gizmos;
if (!show_gizmos) {
RS::get_singleton()->canvas_item_clear(gizmo_canvas_item);
}
queue_redraw();
}
#endif
Expand Down Expand Up @@ -729,7 +733,9 @@ void GPUParticles2D::_notification(int p_what) {

#ifdef TOOLS_ENABLED
if (show_gizmos) {
draw_rect(visibility_rect, Color(0, 0.7, 0.9, 0.4), false);
RS::get_singleton()->canvas_item_clear(gizmo_canvas_item);
Vector<Vector2> pos = Geometry2D::get_rect_outline(visibility_rect);
RS::get_singleton()->canvas_item_add_polyline(gizmo_canvas_item, pos, { Color(0, 0.7, 0.9, 0.4) });
_draw_emission_gizmo();
}
#endif
Expand Down Expand Up @@ -813,42 +819,50 @@ void GPUParticles2D::_draw_emission_gizmo() {
if (pm.is_null()) {
return;
}
draw_set_transform(
Vector2(pm->get_emission_shape_offset().x, pm->get_emission_shape_offset().y),

Transform2D xform(
0.0,
Vector2(pm->get_emission_shape_scale().x, pm->get_emission_shape_scale().y));
Vector2(pm->get_emission_shape_scale().x, pm->get_emission_shape_scale().y),
0.0,
Vector2(pm->get_emission_shape_offset().x, pm->get_emission_shape_offset().y));
RS::get_singleton()->canvas_item_add_set_transform(gizmo_canvas_item, xform);

Vector<Color> colors = { emission_ring_color };
switch (pm->get_emission_shape()) {
case ParticleProcessMaterial::EmissionShape::EMISSION_SHAPE_BOX: {
Vector2 extents2d = Vector2(pm->get_emission_box_extents().x, pm->get_emission_box_extents().y);
draw_rect(Rect2(-extents2d, extents2d * 2.0), emission_ring_color, false);
Vector<Vector2> pos = Geometry2D::get_rect_outline(Rect2(-extents2d, extents2d * 2.0));
RS::get_singleton()->canvas_item_add_polyline(gizmo_canvas_item, pos, colors);
break;
}
case ParticleProcessMaterial::EmissionShape::EMISSION_SHAPE_SPHERE:
case ParticleProcessMaterial::EmissionShape::EMISSION_SHAPE_SPHERE_SURFACE: {
draw_circle(Vector2(), pm->get_emission_sphere_radius(), emission_ring_color, false);
Vector<Vector2> pos = Geometry2D::get_ellipse_outline(Vector2(), pm->get_emission_sphere_radius(), pm->get_emission_sphere_radius());
RS::get_singleton()->canvas_item_add_polyline(gizmo_canvas_item, pos, colors);
break;
}
case ParticleProcessMaterial::EmissionShape::EMISSION_SHAPE_RING: {
Vector3 ring_axis = pm->get_emission_ring_axis();
if (ring_axis.is_equal_approx(Vector3(0.0, 0.0, 1.0)) || ring_axis.is_zero_approx()) {
draw_circle(Vector2(), pm->get_emission_ring_inner_radius(), emission_ring_color, false);
draw_circle(Vector2(), pm->get_emission_ring_radius(), emission_ring_color, false);
Vector<Vector2> pos_inner = Geometry2D::get_ellipse_outline(Vector2(), pm->get_emission_ring_inner_radius(), pm->get_emission_ring_inner_radius());
Vector<Vector2> pos_outer = Geometry2D::get_ellipse_outline(Vector2(), pm->get_emission_ring_radius(), pm->get_emission_ring_radius());
RS::get_singleton()->canvas_item_add_polyline(gizmo_canvas_item, pos_inner, colors);
RS::get_singleton()->canvas_item_add_polyline(gizmo_canvas_item, pos_outer, colors);
} else {
Vector2 a = Vector2(pm->get_emission_ring_height() / -2.0, pm->get_emission_ring_radius() / -1.0);
Vector2 b = Vector2(-a.x, MIN(a.y + std::tan((90.0 - pm->get_emission_ring_cone_angle()) * 0.01745329) * pm->get_emission_ring_height(), 0.0));
Vector2 c = Vector2(b.x, -b.y);
Vector2 d = Vector2(a.x, -a.y);
if (ring_axis.is_equal_approx(Vector3(1.0, 0.0, 0.0))) {
Vector<Vector2> pos = { a, b, b, c, c, d, d, a };
draw_multiline(pos, emission_ring_color);
RS::get_singleton()->canvas_item_add_multiline(gizmo_canvas_item, pos, colors);
} else if (ring_axis.is_equal_approx(Vector3(0.0, 1.0, 0.0))) {
a = Vector2(a.y, a.x);
b = Vector2(b.y, b.x);
c = Vector2(c.y, c.x);
d = Vector2(d.y, d.x);
Vector<Vector2> pos = { a, b, b, c, c, d, d, a };
draw_multiline(pos, emission_ring_color);
RS::get_singleton()->canvas_item_add_multiline(gizmo_canvas_item, pos, colors);
}
}
break;
Expand Down Expand Up @@ -990,6 +1004,12 @@ GPUParticles2D::GPUParticles2D() {
RS::get_singleton()->particles_set_draw_passes(particles, 1);
RS::get_singleton()->particles_set_draw_pass_mesh(particles, 0, mesh);

#ifdef TOOLS_ENABLED
gizmo_canvas_item = RS::get_singleton()->canvas_item_create();
RS::get_singleton()->canvas_item_set_z_index(gizmo_canvas_item, RS::CANVAS_ITEM_Z_MAX - 1);
RS::get_singleton()->canvas_item_set_parent(gizmo_canvas_item, get_canvas_item());
#endif

one_shot = false; // Needed so that set_emitting doesn't access uninitialized values
set_emitting(true);
set_one_shot(false);
Expand All @@ -1016,4 +1036,7 @@ GPUParticles2D::~GPUParticles2D() {
ERR_FAIL_NULL(RenderingServer::get_singleton());
RS::get_singleton()->free_rid(particles);
RS::get_singleton()->free_rid(mesh);
#ifdef TOOLS_ENABLED
RS::get_singleton()->free_rid(gizmo_canvas_item);
#endif
}
1 change: 1 addition & 0 deletions scene/2d/gpu_particles_2d.h
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ class GPUParticles2D : public Node2D {
uint32_t seed = 0;
bool use_fixed_seed = false;
#ifdef TOOLS_ENABLED
RID gizmo_canvas_item;
bool show_gizmos = false;
#endif
Ref<Material> process_material;
Expand Down
27 changes: 3 additions & 24 deletions scene/main/canvas_item.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
#include "canvas_item.h"
#include "canvas_item.compat.inc"

#include "core/math/geometry_2d.h"
#include "scene/2d/canvas_group.h"
#include "scene/main/canvas_layer.h"
#include "scene/main/window.h"
Expand Down Expand Up @@ -827,13 +828,7 @@ void CanvasItem::draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_fil
} else if (p_width >= rect.size.width || p_width >= rect.size.height) {
RenderingServer::get_singleton()->canvas_item_add_rect(canvas_item, rect.grow(0.5f * p_width), p_color, p_antialiased);
} else {
Vector<Vector2> points;
points.resize(5);
points.write[0] = rect.position;
points.write[1] = rect.position + Vector2(rect.size.x, 0);
points.write[2] = rect.position + rect.size;
points.write[3] = rect.position + Vector2(0, rect.size.y);
points.write[4] = rect.position;
Vector<Vector2> points = Geometry2D::get_rect_outline(rect);

Vector<Color> colors = { p_color };

Expand All @@ -844,7 +839,6 @@ void CanvasItem::draw_rect(const Rect2 &p_rect, const Color &p_color, bool p_fil
void CanvasItem::draw_ellipse(const Point2 &p_pos, real_t p_major, real_t p_minor, const Color &p_color, bool p_filled, real_t p_width, bool p_antialiased) {
ERR_THREAD_GUARD;
ERR_DRAW_GUARD;

if (p_filled) {
if (p_width != -1.0) {
WARN_PRINT("The \"width\" argument has no effect when \"filled\" is \"true\".");
Expand All @@ -854,22 +848,7 @@ void CanvasItem::draw_ellipse(const Point2 &p_pos, real_t p_major, real_t p_mino
} else if (p_width >= 2.0 * MAX(p_major, p_minor)) {
RenderingServer::get_singleton()->canvas_item_add_ellipse(canvas_item, p_pos, p_major + 0.5 * p_width, p_minor + 0.5 * p_width, p_color, p_antialiased);
} else {
// Tessellation count is hardcoded. Keep in sync with the same variable in `RendererCanvasCull::canvas_item_add_circle()`.
const int circle_segments = 64;

Vector<Vector2> points;
points.resize(circle_segments + 1);

Vector2 *points_ptr = points.ptrw();
const real_t circle_point_step = Math::TAU / circle_segments;

for (int i = 0; i < circle_segments; i++) {
float angle = i * circle_point_step;
points_ptr[i].x = Math::cos(angle) * p_major;
points_ptr[i].y = Math::sin(angle) * p_minor;
points_ptr[i] += p_pos;
}
points_ptr[circle_segments] = points_ptr[0];
Vector<Vector2> points = Geometry2D::get_ellipse_outline(p_pos, p_major, p_minor);

Vector<Color> colors = { p_color };

Expand Down