From 2da6d82f3bf13e294367f50cf2630ea4dc12be3a Mon Sep 17 00:00:00 2001 From: trollodel <33117082+trollodel@users.noreply.github.com> Date: Wed, 24 Feb 2021 21:23:02 +0100 Subject: [PATCH] Allow CollisionObject to show collision shape meshes Add an editor gizmo to CollisionObject. CollisionShape no longer shows collision shapes directly. --- editor/plugins/spatial_editor_plugin.cpp | 1 + editor/spatial_editor_gizmos.cpp | 51 ++++++++++++++++++++++ editor/spatial_editor_gizmos.h | 13 ++++++ scene/3d/collision_object.cpp | 54 +++++++++++++++++++++++- scene/3d/collision_object.h | 7 +++ scene/3d/collision_shape.cpp | 29 ------------- scene/3d/collision_shape.h | 4 -- 7 files changed, 125 insertions(+), 34 deletions(-) diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp index 483dff7616a9..a83594ef3bd5 100644 --- a/editor/plugins/spatial_editor_plugin.cpp +++ b/editor/plugins/spatial_editor_plugin.cpp @@ -6206,6 +6206,7 @@ void SpatialEditor::_register_all_gizmos() { add_gizmo_plugin(Ref(memnew(ReflectionProbeGizmoPlugin))); add_gizmo_plugin(Ref(memnew(GIProbeGizmoPlugin))); add_gizmo_plugin(Ref(memnew(BakedIndirectLightGizmoPlugin))); + add_gizmo_plugin(Ref(memnew(CollisionObjectGizmoPlugin))); add_gizmo_plugin(Ref(memnew(CollisionShapeSpatialGizmoPlugin))); add_gizmo_plugin(Ref(memnew(CollisionPolygonSpatialGizmoPlugin))); add_gizmo_plugin(Ref(memnew(NavigationMeshSpatialGizmoPlugin))); diff --git a/editor/spatial_editor_gizmos.cpp b/editor/spatial_editor_gizmos.cpp index c60952e5a6c3..db64f88ace0b 100644 --- a/editor/spatial_editor_gizmos.cpp +++ b/editor/spatial_editor_gizmos.cpp @@ -3029,6 +3029,57 @@ void BakedIndirectLightGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { //// +CollisionObjectGizmoPlugin::CollisionObjectGizmoPlugin() { + const Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); + 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); +} + +bool CollisionObjectGizmoPlugin::has_gizmo(Spatial *p_spatial) { + return Object::cast_to(p_spatial) != nullptr; +} + +String CollisionObjectGizmoPlugin::get_name() const { + return "CollisionObject"; +} + +int CollisionObjectGizmoPlugin::get_priority() const { + return -1; +} + +void CollisionObjectGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) { + CollisionObject *co = Object::cast_to(p_gizmo->get_spatial_node()); + + p_gizmo->clear(); + + List owners; + co->get_shape_owners(&owners); + for (List::Element *E = owners.front(); E; E = E->next()) { + uint32_t owner_id = E->get(); + Transform xform = co->shape_owner_get_transform(owner_id); + Object *owner = co->shape_owner_get_owner(owner_id); + // Exclude CollisionShape and CollisionPolygon as they have their gizmo. + if (!Object::cast_to(owner) && !Object::cast_to(owner)) { + Ref material = get_material(!co->is_shape_owner_disabled(owner_id) ? "shape_material" : "shape_material_disabled", p_gizmo); + for (int shape_id = 0; shape_id < co->shape_owner_get_shape_count(owner_id); shape_id++) { + Ref s = co->shape_owner_get_shape(owner_id, shape_id); + if (s.is_null()) { + continue; + } + SurfaceTool st; + st.append_from(s->get_debug_mesh(), 0, xform); + + p_gizmo->add_mesh(st.commit(), false, Ref(), material); + p_gizmo->add_collision_segments(s->get_debug_mesh_lines()); + } + } + } +} + +//// + CollisionShapeSpatialGizmoPlugin::CollisionShapeSpatialGizmoPlugin() { const Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1)); create_material("shape_material", gizmo_color); diff --git a/editor/spatial_editor_gizmos.h b/editor/spatial_editor_gizmos.h index 58598d1ff38c..9a886f592274 100644 --- a/editor/spatial_editor_gizmos.h +++ b/editor/spatial_editor_gizmos.h @@ -321,6 +321,19 @@ class BakedIndirectLightGizmoPlugin : public EditorSpatialGizmoPlugin { BakedIndirectLightGizmoPlugin(); }; +class CollisionObjectGizmoPlugin : public EditorSpatialGizmoPlugin { + + GDCLASS(CollisionObjectGizmoPlugin, EditorSpatialGizmoPlugin); + +public: + bool has_gizmo(Spatial *p_spatial); + String get_name() const; + int get_priority() const; + void redraw(EditorSpatialGizmo *p_gizmo); + + CollisionObjectGizmoPlugin(); +}; + class CollisionShapeSpatialGizmoPlugin : public EditorSpatialGizmoPlugin { GDCLASS(CollisionShapeSpatialGizmoPlugin, EditorSpatialGizmoPlugin); diff --git a/scene/3d/collision_object.cpp b/scene/3d/collision_object.cpp index d3429e9b2815..95ded40e9024 100644 --- a/scene/3d/collision_object.cpp +++ b/scene/3d/collision_object.cpp @@ -30,6 +30,7 @@ #include "collision_object.h" +#include "mesh_instance.h" #include "scene/scene_string_names.h" #include "servers/physics_server.h" @@ -113,6 +114,43 @@ void CollisionObject::_update_pickable() { PhysicsServer::get_singleton()->body_set_ray_pickable(rid, pickable); } +void CollisionObject::_update_debug_shapes() { + for (Set::Element *shapedata_idx = debug_shapes_to_update.front(); shapedata_idx; shapedata_idx = shapedata_idx->next()) { + if (shapes.has(shapedata_idx->get())) { + ShapeData &shapedata = shapes[shapedata_idx->get()]; + for (int i = 0; i < shapedata.shapes.size(); i++) { + ShapeData::ShapeBase &s = shapedata.shapes.write[i]; + if (s.debug_shape) { + s.debug_shape->queue_delete(); + s.debug_shape = nullptr; + } + if (s.shape.is_null() || shapedata.disabled) { + continue; + } + + Ref mesh = s.shape->get_debug_mesh(); + MeshInstance *mi = memnew(MeshInstance); + mi->set_transform(shapedata.xform); + mi->set_mesh(mesh); + add_child(mi); + + mi->force_update_transform(); + s.debug_shape = mi; + } + } + } + debug_shapes_to_update.clear(); +} + +void CollisionObject::_update_shape_data(uint32_t p_owner) { + if (is_inside_tree() && get_tree()->is_debugging_collisions_hint()) { + if (debug_shapes_to_update.empty()) { + call_deferred("_update_debug_shapes"); + } + debug_shapes_to_update.insert(p_owner); + } +} + void CollisionObject::set_ray_pickable(bool p_ray_pickable) { ray_pickable = p_ray_pickable; @@ -147,6 +185,8 @@ void CollisionObject::_bind_methods() { ClassDB::bind_method(D_METHOD("shape_owner_clear_shapes", "owner_id"), &CollisionObject::shape_owner_clear_shapes); ClassDB::bind_method(D_METHOD("shape_find_owner", "shape_index"), &CollisionObject::shape_find_owner); + ClassDB::bind_method(D_METHOD("_update_debug_shapes"), &CollisionObject::_update_debug_shapes); + BIND_VMETHOD(MethodInfo("_input_event", PropertyInfo(Variant::OBJECT, "camera"), PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"), PropertyInfo(Variant::VECTOR3, "click_position"), PropertyInfo(Variant::VECTOR3, "click_normal"), PropertyInfo(Variant::INT, "shape_idx"))); ADD_SIGNAL(MethodInfo("input_event", PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"), PropertyInfo(Variant::VECTOR3, "click_position"), PropertyInfo(Variant::VECTOR3, "click_normal"), PropertyInfo(Variant::INT, "shape_idx"))); @@ -196,6 +236,7 @@ void CollisionObject::shape_owner_set_disabled(uint32_t p_owner, bool p_disabled PhysicsServer::get_singleton()->body_set_shape_disabled(rid, sd.shapes[i].index, p_disabled); } } + _update_shape_data(p_owner); } bool CollisionObject::is_shape_owner_disabled(uint32_t p_owner) const { @@ -235,6 +276,8 @@ void CollisionObject::shape_owner_set_transform(uint32_t p_owner, const Transfor PhysicsServer::get_singleton()->body_set_shape_transform(rid, sd.shapes[i].index, p_transform); } } + + _update_shape_data(p_owner); } Transform CollisionObject::shape_owner_get_transform(uint32_t p_owner) const { @@ -259,6 +302,7 @@ void CollisionObject::shape_owner_add_shape(uint32_t p_owner, const Ref & ShapeData::ShapeBase s; s.index = total_subshapes; s.shape = p_shape; + if (area) { PhysicsServer::get_singleton()->area_add_shape(rid, p_shape->get_rid(), sd.xform, sd.disabled); } else { @@ -267,6 +311,8 @@ void CollisionObject::shape_owner_add_shape(uint32_t p_owner, const Ref & sd.shapes.push_back(s); total_subshapes++; + + _update_shape_data(p_owner); } int CollisionObject::shape_owner_get_shape_count(uint32_t p_owner) const { @@ -294,13 +340,19 @@ void CollisionObject::shape_owner_remove_shape(uint32_t p_owner, int p_shape) { ERR_FAIL_COND(!shapes.has(p_owner)); ERR_FAIL_INDEX(p_shape, shapes[p_owner].shapes.size()); - int index_to_remove = shapes[p_owner].shapes[p_shape].index; + const ShapeData::ShapeBase &s = shapes[p_owner].shapes[p_shape]; + int index_to_remove = s.index; + if (area) { PhysicsServer::get_singleton()->area_remove_shape(rid, index_to_remove); } else { PhysicsServer::get_singleton()->body_remove_shape(rid, index_to_remove); } + if (s.debug_shape) { + s.debug_shape->queue_delete(); + } + shapes[p_owner].shapes.remove(p_shape); for (Map::Element *E = shapes.front(); E; E = E->next()) { diff --git a/scene/3d/collision_object.h b/scene/3d/collision_object.h index 8e9210a6d3bd..8583e99a198f 100644 --- a/scene/3d/collision_object.h +++ b/scene/3d/collision_object.h @@ -47,6 +47,7 @@ class CollisionObject : public Spatial { Object *owner; Transform xform; struct ShapeBase { + Node *debug_shape = nullptr; Ref shape; int index; }; @@ -67,8 +68,12 @@ class CollisionObject : public Spatial { bool capture_input_on_drag; bool ray_pickable; + Set debug_shapes_to_update; + void _update_pickable(); + void _update_shape_data(uint32_t p_owner); + protected: CollisionObject(RID p_rid, bool p_area); @@ -79,6 +84,8 @@ class CollisionObject : public Spatial { virtual void _mouse_enter(); virtual void _mouse_exit(); + void _update_debug_shapes(); + public: uint32_t create_shape_owner(Object *p_owner); void remove_shape_owner(uint32_t owner); diff --git a/scene/3d/collision_shape.cpp b/scene/3d/collision_shape.cpp index 8b106a747d4d..63c9a0373919 100644 --- a/scene/3d/collision_shape.cpp +++ b/scene/3d/collision_shape.cpp @@ -89,9 +89,6 @@ void CollisionShape::_notification(int p_what) { if (parent) { _update_in_shape_owner(); } - if (get_tree()->is_debugging_collisions_hint()) { - _update_debug_shape(); - } } break; case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { if (parent) { @@ -162,7 +159,6 @@ void CollisionShape::_bind_methods() { ClassDB::set_method_flags("CollisionShape", "make_convex_from_brothers", METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR); ClassDB::bind_method(D_METHOD("_shape_changed"), &CollisionShape::_shape_changed); - ClassDB::bind_method(D_METHOD("_update_debug_shape"), &CollisionShape::_update_debug_shape); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape"), "set_shape", "get_shape"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled"); @@ -217,7 +213,6 @@ CollisionShape::CollisionShape() { //indicator = VisualServer::get_singleton()->mesh_create(); disabled = false; - debug_shape = NULL; parent = NULL; owner_id = 0; set_notify_local_transform(true); @@ -229,33 +224,9 @@ CollisionShape::~CollisionShape() { //VisualServer::get_singleton()->free(indicator); } -void CollisionShape::_update_debug_shape() { - debug_shape_dirty = false; - - if (debug_shape) { - debug_shape->queue_delete(); - debug_shape = NULL; - } - - Ref s = get_shape(); - if (s.is_null()) - return; - - Ref mesh = s->get_debug_mesh(); - MeshInstance *mi = memnew(MeshInstance); - mi->set_mesh(mesh); - add_child(mi); - debug_shape = mi; -} - void CollisionShape::_shape_changed() { // If this is a heightfield shape our center may have changed if (parent) { _update_in_shape_owner(true); } - - if (is_inside_tree() && get_tree()->is_debugging_collisions_hint() && !debug_shape_dirty) { - debug_shape_dirty = true; - call_deferred("_update_debug_shape"); - } } diff --git a/scene/3d/collision_shape.h b/scene/3d/collision_shape.h index 35f2de7e0df0..fc331315d299 100644 --- a/scene/3d/collision_shape.h +++ b/scene/3d/collision_shape.h @@ -44,14 +44,10 @@ class CollisionShape : public Spatial { uint32_t owner_id; CollisionObject *parent; - Node *debug_shape; - bool debug_shape_dirty; - void resource_changed(RES res); bool disabled; protected: - void _update_debug_shape(); void _shape_changed(); void _update_in_shape_owner(bool p_xform_only = false);