Skip to content

Commit

Permalink
Allow CollisionObject3D to show collision shape meshes
Browse files Browse the repository at this point in the history
Add an editor gizmo to CollisionObject3D.
CollisionShape3D no longer shows collision shapes directly.
  • Loading branch information
trollodel committed Feb 24, 2021
1 parent 65305ea commit 85a776d
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 85a776d

Please sign in to comment.