From a3ef9de6a86fd4a313b8948df1237f13852b85d4 Mon Sep 17 00:00:00 2001 From: BattyBovine Date: Sat, 18 Jan 2025 17:37:06 -0500 Subject: [PATCH] Add debug colours and fills to CollisionPolygon3D. This brings CollisionPolygon3D up to feature parity with its counterpart CollisionShape3D. Closes #101414. In addition, adding this feature fixes the issue that CollisionPolygon3D would never be rendered in exported builds, even if Visible Collision Shapes is enabled at runtime. Closes #101413. --- doc/classes/CollisionPolygon3D.xml | 7 + .../collision_polygon_3d_gizmo_plugin.cpp | 169 +++++++++++++++++- .../collision_polygon_3d_gizmo_plugin.h | 2 + scene/3d/physics/collision_polygon_3d.cpp | 77 ++++++++ scene/3d/physics/collision_polygon_3d.h | 17 ++ 5 files changed, 264 insertions(+), 8 deletions(-) diff --git a/doc/classes/CollisionPolygon3D.xml b/doc/classes/CollisionPolygon3D.xml index 4f5866c3484..5758e179967 100644 --- a/doc/classes/CollisionPolygon3D.xml +++ b/doc/classes/CollisionPolygon3D.xml @@ -10,6 +10,13 @@ + + The collision shape color that is displayed in the editor, or in the running project if [b]Debug > Visible Collision Shapes[/b] is checked at the top of the editor. + [b]Note:[/b] The default value is [member ProjectSettings.debug/shapes/collision/shape_color]. The [code]Color(0, 0, 0, 0)[/code] value documented here is a placeholder, and not the actual default debug color. + + + If [code]true[/code], when the shape is displayed, it will show a solid fill color in addition to its wireframe. + Length that the resulting collision extends in either direction perpendicular to its 2D polygon. diff --git a/editor/plugins/gizmos/collision_polygon_3d_gizmo_plugin.cpp b/editor/plugins/gizmos/collision_polygon_3d_gizmo_plugin.cpp index 4f9b93c9121..2a5dd571c06 100644 --- a/editor/plugins/gizmos/collision_polygon_3d_gizmo_plugin.cpp +++ b/editor/plugins/gizmos/collision_polygon_3d_gizmo_plugin.cpp @@ -30,14 +30,44 @@ #include "collision_polygon_3d_gizmo_plugin.h" +#include "core/math/geometry_2d.h" #include "scene/3d/physics/collision_polygon_3d.h" CollisionPolygon3DGizmoPlugin::CollisionPolygon3DGizmoPlugin() { - const Color gizmo_color = SceneTree::get_singleton()->get_debug_collisions_color(); - create_material("shape_material", gizmo_color); - const float gizmo_value = gizmo_color.get_v(); - const Color gizmo_color_disabled = Color(gizmo_value, gizmo_value, gizmo_value, 0.65); - create_material("shape_material_disabled", gizmo_color_disabled); + create_collision_material("shape_material", 2.0); + create_collision_material("shape_material_arraymesh", 0.0625); + + create_collision_material("shape_material_disabled", 0.0625); + create_collision_material("shape_material_arraymesh_disabled", 0.015625); +} + +void CollisionPolygon3DGizmoPlugin::create_collision_material(const String &p_name, float p_alpha) { + Vector> mats; + + const Color collision_color(1.0, 1.0, 1.0, p_alpha); + + for (int i = 0; i < 4; i++) { + bool instantiated = i < 2; + + Ref material; + material.instantiate(); + + Color color = collision_color; + color.a *= instantiated ? 0.25 : 1.0; + + material->set_albedo(color); + material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); + material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA); + material->set_render_priority(StandardMaterial3D::RENDER_PRIORITY_MIN + 1); + material->set_cull_mode(StandardMaterial3D::CULL_BACK); + material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true); + material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); + material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); + + mats.push_back(material); + } + + materials[p_name] = mats; } bool CollisionPolygon3DGizmoPlugin::has_gizmo(Node3D *p_spatial) { @@ -57,6 +87,13 @@ void CollisionPolygon3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { p_gizmo->clear(); + const Ref material = + get_material(!polygon->is_disabled() ? "shape_material" : "shape_material_disabled", p_gizmo); + const Ref material_arraymesh = + get_material(!polygon->is_disabled() ? "shape_material_arraymesh" : "shape_material_arraymesh_disabled", p_gizmo); + + const Color collision_color = polygon->is_disabled() ? Color(1.0, 1.0, 1.0, 0.75) : polygon->get_debug_color(); + Vector points = polygon->get_polygon(); float depth = polygon->get_depth() * 0.5; @@ -71,9 +108,125 @@ void CollisionPolygon3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) { lines.push_back(Vector3(points[i].x, points[i].y, -depth)); } - const Ref material = - get_material(!polygon->is_disabled() ? "shape_material" : "shape_material_disabled", p_gizmo); + if (polygon->get_debug_fill_enabled()) { + Ref array_mesh; + array_mesh.instantiate(); - p_gizmo->add_lines(lines, material); + Vector verts; + Vector colors; + Vector indices; + + // Determine orientation of the 2D polygon's vertices to determine + // which direction to draw outer polygons. + float signed_area = 0.0f; + for (int i = 0; i < points.size(); i++) { + const int j = (i + 1) % points.size(); + signed_area += points[i].x * points[j].y - points[j].x * points[i].y; + } + + // Generate triangles for the sides of the extruded polygon. + for (int i = 0; i < points.size(); i++) { + verts.push_back(Vector3(points[i].x, points[i].y, depth)); + verts.push_back(Vector3(points[i].x, points[i].y, -depth)); + + colors.push_back(collision_color); + colors.push_back(collision_color); + } + + for (int i = 0; i < verts.size(); i += 2) { + const int j = (i + 1) % verts.size(); + const int k = (i + 2) % verts.size(); + const int l = (i + 3) % verts.size(); + + indices.push_back(i); + if (signed_area < 0) { + indices.push_back(j); + indices.push_back(k); + } else { + indices.push_back(k); + indices.push_back(j); + } + + indices.push_back(j); + if (signed_area < 0) { + indices.push_back(l); + indices.push_back(k); + } else { + indices.push_back(k); + indices.push_back(l); + } + } + + Vector> decomp = Geometry2D::decompose_polygon_in_convex(polygon->get_polygon()); + + // Generate triangles for the bottom cap of the extruded polygon. + for (int i = 0; i < decomp.size(); i++) { + Vector cap_verts_bottom; + Vector cap_colours_bottom; + Vector cap_indices_bottom; + + const int index_offset = verts.size(); + + const Vector &convex = decomp[i]; + + for (int j = 0; j < convex.size(); j++) { + cap_verts_bottom.push_back(Vector3(convex[j].x, convex[j].y, -depth)); + cap_colours_bottom.push_back(collision_color); + } + + if (convex.size() >= 3) { + for (int j = 1; j < convex.size(); j++) { + const int k = (j + 1) % convex.size(); + + cap_indices_bottom.push_back(index_offset + 0); + cap_indices_bottom.push_back(index_offset + j); + cap_indices_bottom.push_back(index_offset + k); + } + } + verts.append_array(cap_verts_bottom); + colors.append_array(cap_colours_bottom); + indices.append_array(cap_indices_bottom); + } + + // Generate triangles for the top cap of the extruded polygon. + for (int i = 0; i < decomp.size(); i++) { + Vector cap_verts_top; + Vector cap_colours_top; + Vector cap_indices_top; + + const int index_offset = verts.size(); + + const Vector &convex = decomp[i]; + + for (int j = 0; j < convex.size(); j++) { + cap_verts_top.push_back(Vector3(convex[j].x, convex[j].y, depth)); + cap_colours_top.push_back(collision_color); + } + + if (convex.size() >= 3) { + for (int j = 1; j < convex.size(); j++) { + const int k = (j + 1) % convex.size(); + + cap_indices_top.push_back(index_offset + k); + cap_indices_top.push_back(index_offset + j); + cap_indices_top.push_back(index_offset + 0); + } + } + verts.append_array(cap_verts_top); + colors.append_array(cap_colours_top); + indices.append_array(cap_indices_top); + } + + Array a; + a.resize(Mesh::ARRAY_MAX); + a[RS::ARRAY_VERTEX] = verts; + a[RS::ARRAY_COLOR] = colors; + a[RS::ARRAY_INDEX] = indices; + array_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, a); + + p_gizmo->add_mesh(array_mesh, material_arraymesh); + } + + p_gizmo->add_lines(lines, material, false, collision_color); p_gizmo->add_collision_segments(lines); } diff --git a/editor/plugins/gizmos/collision_polygon_3d_gizmo_plugin.h b/editor/plugins/gizmos/collision_polygon_3d_gizmo_plugin.h index b8342a25ca5..6c78a862cfc 100644 --- a/editor/plugins/gizmos/collision_polygon_3d_gizmo_plugin.h +++ b/editor/plugins/gizmos/collision_polygon_3d_gizmo_plugin.h @@ -36,6 +36,8 @@ class CollisionPolygon3DGizmoPlugin : public EditorNode3DGizmoPlugin { GDCLASS(CollisionPolygon3DGizmoPlugin, EditorNode3DGizmoPlugin); + void create_collision_material(const String &p_name, float p_alpha); + public: bool has_gizmo(Node3D *p_spatial) override; String get_gizmo_name() const override; diff --git a/scene/3d/physics/collision_polygon_3d.cpp b/scene/3d/physics/collision_polygon_3d.cpp index bf8dec7b546..51945452ab0 100644 --- a/scene/3d/physics/collision_polygon_3d.cpp +++ b/scene/3d/physics/collision_polygon_3d.cpp @@ -70,6 +70,8 @@ void CollisionPolygon3D::_build_polygon() { convex->set_points(cp); convex->set_margin(margin); + convex->set_debug_color(debug_color); + convex->set_debug_fill(debug_fill); collision_object->shape_owner_add_shape(owner_id, convex); collision_object->shape_owner_set_disabled(owner_id, disabled); } @@ -157,6 +159,68 @@ bool CollisionPolygon3D::is_disabled() const { return disabled; } +Color CollisionPolygon3D::_get_default_debug_color() const { + const SceneTree *st = SceneTree::get_singleton(); + return st ? st->get_debug_collisions_color() : Color(0.0, 0.0, 0.0, 0.0); +} + +void CollisionPolygon3D::set_debug_color(const Color &p_color) { + if (debug_color == p_color) { + return; + } + + debug_color = p_color; + + update_gizmos(); +} + +Color CollisionPolygon3D::get_debug_color() const { + return debug_color; +} + +void CollisionPolygon3D::set_debug_fill_enabled(bool p_enable) { + if (debug_fill == p_enable) { + return; + } + + debug_fill = p_enable; + + update_gizmos(); +} + +bool CollisionPolygon3D::get_debug_fill_enabled() const { + return debug_fill; +} + +#ifdef DEBUG_ENABLED + +bool CollisionPolygon3D::_property_can_revert(const StringName &p_name) const { + if (p_name == "debug_color") { + return true; + } + return false; +} + +bool CollisionPolygon3D::_property_get_revert(const StringName &p_name, Variant &r_property) const { + if (p_name == "debug_color") { + r_property = _get_default_debug_color(); + return true; + } + return false; +} + +void CollisionPolygon3D::_validate_property(PropertyInfo &p_property) const { + if (p_property.name == "debug_color") { + if (debug_color == _get_default_debug_color()) { + p_property.usage = PROPERTY_USAGE_DEFAULT & ~PROPERTY_USAGE_STORAGE; + } else { + p_property.usage = PROPERTY_USAGE_DEFAULT; + } + } +} + +#endif // DEBUG_ENABLED + real_t CollisionPolygon3D::get_margin() const { return margin; } @@ -201,6 +265,12 @@ void CollisionPolygon3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_disabled", "disabled"), &CollisionPolygon3D::set_disabled); ClassDB::bind_method(D_METHOD("is_disabled"), &CollisionPolygon3D::is_disabled); + ClassDB::bind_method(D_METHOD("set_debug_color", "color"), &CollisionPolygon3D::set_debug_color); + ClassDB::bind_method(D_METHOD("get_debug_color"), &CollisionPolygon3D::get_debug_color); + + ClassDB::bind_method(D_METHOD("set_enable_debug_fill", "enable"), &CollisionPolygon3D::set_debug_fill_enabled); + ClassDB::bind_method(D_METHOD("get_enable_debug_fill"), &CollisionPolygon3D::get_debug_fill_enabled); + ClassDB::bind_method(D_METHOD("set_margin", "margin"), &CollisionPolygon3D::set_margin); ClassDB::bind_method(D_METHOD("get_margin"), &CollisionPolygon3D::get_margin); @@ -210,8 +280,15 @@ void CollisionPolygon3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled"); ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "polygon"), "set_polygon", "get_polygon"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "margin", PROPERTY_HINT_RANGE, "0.001,10,0.001,suffix:m"), "set_margin", "get_margin"); + + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "debug_color"), "set_debug_color", "get_debug_color"); + // Default value depends on a project setting, override for doc generation purposes. + ADD_PROPERTY_DEFAULT("debug_color", Color(0.0, 0.0, 0.0, 0.0)); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "debug_fill"), "set_enable_debug_fill", "get_enable_debug_fill"); } CollisionPolygon3D::CollisionPolygon3D() { set_notify_local_transform(true); + debug_color = _get_default_debug_color(); } diff --git a/scene/3d/physics/collision_polygon_3d.h b/scene/3d/physics/collision_polygon_3d.h index 48c5284ae33..ace69b211eb 100644 --- a/scene/3d/physics/collision_polygon_3d.h +++ b/scene/3d/physics/collision_polygon_3d.h @@ -46,6 +46,11 @@ protected: uint32_t owner_id = 0; CollisionObject3D *collision_object = nullptr; + Color debug_color; + bool debug_fill = true; + + Color _get_default_debug_color() const; + bool disabled = false; void _build_polygon(); @@ -58,6 +63,12 @@ protected: void _notification(int p_what); static void _bind_methods(); +#ifdef DEBUG_ENABLED + bool _property_can_revert(const StringName &p_name) const; + bool _property_get_revert(const StringName &p_name, Variant &r_property) const; + void _validate_property(PropertyInfo &p_property) const; +#endif // DEBUG_ENABLED + public: void set_depth(real_t p_depth); real_t get_depth() const; @@ -68,6 +79,12 @@ public: void set_disabled(bool p_disabled); bool is_disabled() const; + void set_debug_color(const Color &p_color); + Color get_debug_color() const; + + void set_debug_fill_enabled(bool p_enable); + bool get_debug_fill_enabled() const; + virtual AABB get_item_rect() const; real_t get_margin() const;