Skip to content

Commit

Permalink
Merge pull request #45783 from trollodel/collisionobject3d-debug-shapes
Browse files Browse the repository at this point in the history
Allow CollisionObject3D to show collision shape meshes
  • Loading branch information
akien-mga authored Feb 24, 2021
2 parents 65305ea + 85a776d commit 4cdd222
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 35 deletions.
51 changes: 51 additions & 0 deletions editor/node_3d_editor_gizmos.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3505,6 +3505,57 @@ void LightmapProbeGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {

////

CollisionObject3DGizmoPlugin::CollisionObject3DGizmoPlugin() {
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 CollisionObject3DGizmoPlugin::has_gizmo(Node3D *p_spatial) {
return Object::cast_to<CollisionObject3D>(p_spatial) != nullptr;
}

String CollisionObject3DGizmoPlugin::get_gizmo_name() const {
return "CollisionObject3D";
}

int CollisionObject3DGizmoPlugin::get_priority() const {
return -1;
}

void CollisionObject3DGizmoPlugin::redraw(EditorNode3DGizmo *p_gizmo) {
CollisionObject3D *co = Object::cast_to<CollisionObject3D>(p_gizmo->get_spatial_node());

p_gizmo->clear();

List<uint32_t> owners;
co->get_shape_owners(&owners);
for (List<uint32_t>::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 CollisionShape3D and CollisionPolygon3D as they have their gizmo.
if (!Object::cast_to<CollisionShape3D>(owner) && !Object::cast_to<CollisionPolygon3D>(owner)) {
Ref<Material> 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<Shape3D> 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<SkinReference>(), material);
p_gizmo->add_collision_segments(s->get_debug_mesh_lines());
}
}
}
}

////

CollisionShape3DGizmoPlugin::CollisionShape3DGizmoPlugin() {
const Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1));
create_material("shape_material", gizmo_color);
Expand Down
12 changes: 12 additions & 0 deletions editor/node_3d_editor_gizmos.h
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,18 @@ class LightmapProbeGizmoPlugin : public EditorNode3DGizmoPlugin {
LightmapProbeGizmoPlugin();
};

class CollisionObject3DGizmoPlugin : public EditorNode3DGizmoPlugin {
GDCLASS(CollisionObject3DGizmoPlugin, EditorNode3DGizmoPlugin);

public:
bool has_gizmo(Node3D *p_spatial) override;
String get_gizmo_name() const override;
int get_priority() const override;
void redraw(EditorNode3DGizmo *p_gizmo) override;

CollisionObject3DGizmoPlugin();
};

class CollisionShape3DGizmoPlugin : public EditorNode3DGizmoPlugin {
GDCLASS(CollisionShape3DGizmoPlugin, EditorNode3DGizmoPlugin);

Expand Down
1 change: 1 addition & 0 deletions editor/plugins/node_3d_editor_plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6452,6 +6452,7 @@ void Node3DEditor::_register_all_gizmos() {
add_gizmo_plugin(Ref<GIProbeGizmoPlugin>(memnew(GIProbeGizmoPlugin)));
add_gizmo_plugin(Ref<BakedLightmapGizmoPlugin>(memnew(BakedLightmapGizmoPlugin)));
add_gizmo_plugin(Ref<LightmapProbeGizmoPlugin>(memnew(LightmapProbeGizmoPlugin)));
add_gizmo_plugin(Ref<CollisionObject3DGizmoPlugin>(memnew(CollisionObject3DGizmoPlugin)));
add_gizmo_plugin(Ref<CollisionShape3DGizmoPlugin>(memnew(CollisionShape3DGizmoPlugin)));
add_gizmo_plugin(Ref<CollisionPolygon3DGizmoPlugin>(memnew(CollisionPolygon3DGizmoPlugin)));
add_gizmo_plugin(Ref<NavigationRegion3DGizmoPlugin>(memnew(NavigationRegion3DGizmoPlugin)));
Expand Down
53 changes: 52 additions & 1 deletion scene/3d/collision_object_3d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

#include "collision_object_3d.h"

#include "mesh_instance_3d.h"
#include "scene/scene_string_names.h"
#include "servers/physics_server_3d.h"

Expand Down Expand Up @@ -110,6 +111,42 @@ void CollisionObject3D::_update_pickable() {
}
}

void CollisionObject3D::_update_debug_shapes() {
for (Set<uint32_t>::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> mesh = s.shape->get_debug_mesh();
MeshInstance3D *mi = memnew(MeshInstance3D);
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 CollisionObject3D::_update_shape_data(uint32_t p_owner) {
if (is_inside_tree() && get_tree()->is_debugging_collisions_hint()) {
if (debug_shapes_to_update.is_empty()) {
call_deferred("_update_debug_shapes");
}
debug_shapes_to_update.insert(p_owner);
}
}

void CollisionObject3D::set_ray_pickable(bool p_ray_pickable) {
ray_pickable = p_ray_pickable;
_update_pickable();
Expand Down Expand Up @@ -141,6 +178,8 @@ void CollisionObject3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("shape_owner_clear_shapes", "owner_id"), &CollisionObject3D::shape_owner_clear_shapes);
ClassDB::bind_method(D_METHOD("shape_find_owner", "shape_index"), &CollisionObject3D::shape_find_owner);

ClassDB::bind_method(D_METHOD("_update_debug_shapes"), &CollisionObject3D::_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")));
Expand Down Expand Up @@ -188,6 +227,7 @@ void CollisionObject3D::shape_owner_set_disabled(uint32_t p_owner, bool p_disabl
PhysicsServer3D::get_singleton()->body_set_shape_disabled(rid, sd.shapes[i].index, p_disabled);
}
}
_update_shape_data(p_owner);
}

bool CollisionObject3D::is_shape_owner_disabled(uint32_t p_owner) const {
Expand Down Expand Up @@ -223,6 +263,8 @@ void CollisionObject3D::shape_owner_set_transform(uint32_t p_owner, const Transf
PhysicsServer3D::get_singleton()->body_set_shape_transform(rid, sd.shapes[i].index, p_transform);
}
}

_update_shape_data(p_owner);
}

Transform CollisionObject3D::shape_owner_get_transform(uint32_t p_owner) const {
Expand All @@ -245,6 +287,7 @@ void CollisionObject3D::shape_owner_add_shape(uint32_t p_owner, const Ref<Shape3
ShapeData::ShapeBase s;
s.index = total_subshapes;
s.shape = p_shape;

if (area) {
PhysicsServer3D::get_singleton()->area_add_shape(rid, p_shape->get_rid(), sd.xform, sd.disabled);
} else {
Expand All @@ -253,6 +296,8 @@ void CollisionObject3D::shape_owner_add_shape(uint32_t p_owner, const Ref<Shape3
sd.shapes.push_back(s);

total_subshapes++;

_update_shape_data(p_owner);
}

int CollisionObject3D::shape_owner_get_shape_count(uint32_t p_owner) const {
Expand All @@ -279,13 +324,19 @@ void CollisionObject3D::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) {
PhysicsServer3D::get_singleton()->area_remove_shape(rid, index_to_remove);
} else {
PhysicsServer3D::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<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) {
Expand Down
7 changes: 7 additions & 0 deletions scene/3d/collision_object_3d.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class CollisionObject3D : public Node3D {
Object *owner = nullptr;
Transform xform;
struct ShapeBase {
Node *debug_shape = nullptr;
Ref<Shape3D> shape;
int index = 0;
};
Expand All @@ -60,8 +61,12 @@ class CollisionObject3D : public Node3D {
bool capture_input_on_drag = false;
bool ray_pickable = true;

Set<uint32_t> debug_shapes_to_update;

void _update_pickable();

void _update_shape_data(uint32_t p_owner);

protected:
CollisionObject3D(RID p_rid, bool p_area);

Expand All @@ -72,6 +77,8 @@ class CollisionObject3D : public Node3D {
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);
Expand Down
30 changes: 0 additions & 30 deletions scene/3d/collision_shape_3d.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,6 @@ void CollisionShape3D::_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) {
Expand Down Expand Up @@ -163,8 +160,6 @@ void CollisionShape3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("make_convex_from_siblings"), &CollisionShape3D::make_convex_from_siblings);
ClassDB::set_method_flags("CollisionShape3D", "make_convex_from_siblings", METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR);

ClassDB::bind_method(D_METHOD("_update_debug_shape"), &CollisionShape3D::_update_debug_shape);

ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape3D"), "set_shape", "get_shape");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled");
}
Expand Down Expand Up @@ -224,34 +219,9 @@ CollisionShape3D::~CollisionShape3D() {
//RenderingServer::get_singleton()->free(indicator);
}

void CollisionShape3D::_update_debug_shape() {
debug_shape_dirty = false;

if (debug_shape) {
debug_shape->queue_delete();
debug_shape = nullptr;
}

Ref<Shape3D> s = get_shape();
if (s.is_null()) {
return;
}

Ref<Mesh> mesh = s->get_debug_mesh();
MeshInstance3D *mi = memnew(MeshInstance3D);
mi->set_mesh(mesh);
add_child(mi);
debug_shape = mi;
}

void CollisionShape3D::_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");
}
}
4 changes: 0 additions & 4 deletions scene/3d/collision_shape_3d.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,10 @@ class CollisionShape3D : public Node3D {
uint32_t owner_id = 0;
CollisionObject3D *parent = nullptr;

Node *debug_shape = nullptr;
bool debug_shape_dirty;

void resource_changed(RES res);
bool disabled = false;

protected:
void _update_debug_shape();
void _shape_changed();

void _update_in_shape_owner(bool p_xform_only = false);
Expand Down

0 comments on commit 4cdd222

Please sign in to comment.