diff --git a/modules/gridmap/doc_classes/GridMap.xml b/modules/gridmap/doc_classes/GridMap.xml
index 12417c0d8a..490f5de16e 100644
--- a/modules/gridmap/doc_classes/GridMap.xml
+++ b/modules/gridmap/doc_classes/GridMap.xml
@@ -268,6 +268,12 @@
Show or hide the [GridMap]'s collision shapes. If set to [constant DEBUG_VISIBILITY_MODE_DEFAULT], this depends on the show collision debug settings.
+
+ The [Color] used for rendering octant debug visuals when [member debug_show_octants] is enabled.
+
+
+ If [code]true[/code], shows debug visuals for octants.
+
The assigned [MeshLibrary].
diff --git a/modules/gridmap/grid_map.cpp b/modules/gridmap/grid_map.cpp
index 3b3f920a28..c4ad64dce2 100644
--- a/modules/gridmap/grid_map.cpp
+++ b/modules/gridmap/grid_map.cpp
@@ -380,6 +380,134 @@ bool GridMap::get_center_z() const {
return center_z;
}
+void GridMap::set_debug_octant_color(const Color &p_color) {
+#ifdef DEBUG_ENABLED
+ if (debug_octant_color == p_color) {
+ return;
+ }
+
+ debug_octant_color = p_color;
+
+ if (debug_octant_line_material.is_valid()) {
+ debug_octant_line_material->set_albedo(debug_octant_color);
+ }
+#endif
+}
+
+Color GridMap::get_debug_octant_color() const {
+ return debug_octant_color;
+}
+
+#ifdef DEBUG_ENABLED
+void GridMap::_debug_update() {
+ if (debug_dirty || !is_inside_tree()) {
+ return;
+ }
+ debug_dirty = true;
+
+ callable_mp(this, &GridMap::_debug_update_octants).call_deferred();
+}
+
+void GridMap::_debug_update_octants() {
+ debug_dirty = false;
+
+ if (!debug_show_octants) {
+ _debug_clear_octants();
+ return;
+ }
+
+ if (debug_octant_line_material.is_null()) {
+ debug_octant_line_material.instantiate();
+ debug_octant_line_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED);
+ debug_octant_line_material->set_transparency(StandardMaterial3D::TRANSPARENCY_ALPHA);
+ debug_octant_line_material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
+ debug_octant_line_material->set_albedo(debug_octant_color);
+ }
+
+ Array debug_mesh_arrays = _build_octant_line_mesh_arrays();
+
+ if (debug_octant_line_mesh_rid.is_null()) {
+ debug_octant_line_mesh_rid = RS::get_singleton()->mesh_create();
+ }
+ RS::get_singleton()->mesh_clear(debug_octant_line_mesh_rid);
+ RS::get_singleton()->mesh_add_surface_from_arrays(debug_octant_line_mesh_rid, RSE::PRIMITIVE_LINES, debug_mesh_arrays);
+ RS::get_singleton()->mesh_surface_set_material(debug_octant_line_mesh_rid, 0, debug_octant_line_material->get_rid());
+
+ RID debug_scenario = get_world_3d()->get_scenario();
+ const Transform3D &gridmap_xform = get_global_transform();
+
+ for (const KeyValue &ele : octant_map) {
+ OctantKey octant_key = ele.key;
+ HashMap::Iterator E = debug_octant_map.find(octant_key);
+ if (!E) {
+ OctantDebug *octant_debug = memnew(OctantDebug);
+
+ RID debug_octant_line_instance_rid = RS::get_singleton()->instance_create();
+
+ RS::get_singleton()->instance_set_base(debug_octant_line_instance_rid, debug_octant_line_mesh_rid);
+ RS::get_singleton()->instance_set_scenario(debug_octant_line_instance_rid, debug_scenario);
+
+ octant_debug->debug_line_instance_rid = debug_octant_line_instance_rid;
+
+ E = debug_octant_map.insert(octant_key, octant_debug);
+ }
+
+ OctantDebug &octant_debug = *E->value;
+ const Transform3D octant_transform = gridmap_xform * (Transform3D(Basis(), Vector3(octant_key.x, octant_key.y, octant_key.z) * octant_size * cell_size));
+
+ RS::get_singleton()->instance_set_scenario(octant_debug.debug_line_instance_rid, debug_scenario);
+ RS::get_singleton()->instance_set_transform(octant_debug.debug_line_instance_rid, octant_transform);
+ RS::get_singleton()->instance_set_visible(octant_debug.debug_line_instance_rid, is_visible_in_tree());
+ }
+}
+
+void GridMap::_debug_clear_octants() {
+ for (const KeyValue &ele : octant_map) {
+ OctantKey octant_key = ele.key;
+ HashMap::Iterator E = debug_octant_map.find(octant_key);
+ if (E) {
+ OctantDebug &octant_debug = *E->value;
+
+ if (octant_debug.debug_line_mesh_rid.is_valid()) {
+ RS::get_singleton()->free_rid(octant_debug.debug_line_mesh_rid);
+ octant_debug.debug_line_mesh_rid = RID();
+ }
+ if (octant_debug.debug_line_instance_rid.is_valid()) {
+ RS::get_singleton()->free_rid(octant_debug.debug_line_instance_rid);
+ octant_debug.debug_line_instance_rid = RID();
+ }
+
+ memdelete(&octant_debug);
+ }
+ }
+
+ debug_octant_map.clear();
+}
+
+Array GridMap::_build_octant_line_mesh_arrays() const {
+ AABB aabb;
+ aabb.size = octant_size * cell_size;
+
+ Vector vertex_array;
+ vertex_array.resize(24);
+ Vector3 *vertex_array_ptrw = vertex_array.ptrw();
+ int vertex_index = 0;
+
+ for (int i = 0; i < 12; i++) {
+ Vector3 a, b;
+ aabb.get_edge(i, a, b);
+ vertex_array_ptrw[vertex_index++] = a;
+ vertex_array_ptrw[vertex_index++] = b;
+ }
+
+ Array mesh_arrays;
+ mesh_arrays.resize(Mesh::ARRAY_MAX);
+ mesh_arrays[Mesh::ARRAY_VERTEX] = vertex_array;
+
+ return mesh_arrays;
+}
+#endif // DEBUG_ENABLED
+
void GridMap::set_cell_item(const Vector3i &p_position, int p_item, int p_rot) {
if (baked_meshes.size() && !recreating_octants) {
//if you set a cell item, baked meshes go good bye
@@ -1009,6 +1137,8 @@ void GridMap::_octant_exit_world(const OctantKey &p_key) {
g.navigation_debug_edge_connections_mesh.unref();
}
}
+
+ _debug_clear_octants();
#endif // DEBUG_ENABLED
}
@@ -1058,9 +1188,11 @@ void GridMap::_octant_clean_up(const OctantKey &p_key) {
g.navigation_debug_edge_connections_mesh.unref();
}
}
+
+ _debug_clear_octants();
#endif // DEBUG_ENABLED
- //erase multimeshes
+ // Erase multimeshes.
for (int i = 0; i < g.multimesh_instances.size(); i++) {
RS::get_singleton()->free_rid(g.multimesh_instances[i].instance);
@@ -1085,20 +1217,32 @@ void GridMap::_notification(int p_what) {
} break;
case NOTIFICATION_ENTER_TREE: {
-#if defined(DEBUG_ENABLED) && !defined(NAVIGATION_3D_DISABLED)
+#ifdef DEBUG_ENABLED
+ _debug_update();
+
+#ifndef NAVIGATION_3D_DISABLED
if (bake_navigation && NavigationServer3D::get_singleton()->get_debug_navigation_enabled()) {
_update_navigation_debug_edge_connections();
}
-#endif // defined(DEBUG_ENABLED) && !defined(NAVIGATION_3D_DISABLED)
+#endif // NAVIGATION_3D_DISABLED
+
+#endif // DEBUG_ENABLED
+
_update_visibility();
} break;
+#ifdef DEBUG_ENABLED
+ case NOTIFICATION_EXIT_TREE: {
+ _debug_clear_octants();
+ } break;
+#endif
+
case NOTIFICATION_TRANSFORM_CHANGED: {
Transform3D new_xform = get_global_transform();
if (new_xform == last_transform) {
break;
}
- //update run
+
for (const KeyValue &E : octant_map) {
_octant_transform(E.key);
}
@@ -1108,6 +1252,17 @@ void GridMap::_notification(int p_what) {
for (int i = 0; i < baked_meshes.size(); i++) {
RS::get_singleton()->instance_set_transform(baked_meshes[i].instance, get_global_transform());
}
+
+#ifdef DEBUG_ENABLED
+ for (const KeyValue &E : debug_octant_map) {
+ OctantKey octant_key = E.key;
+ OctantDebug &octant_debug = *E.value;
+ if (octant_debug.debug_line_instance_rid.is_valid()) {
+ const Transform3D octant_transform = new_xform * (Transform3D(Basis(), Vector3(octant_key.x, octant_key.y, octant_key.z) * octant_size * cell_size));
+ RS::get_singleton()->instance_set_transform(octant_debug.debug_line_instance_rid, octant_transform);
+ }
+ }
+#endif
} break;
case NOTIFICATION_EXIT_WORLD: {
@@ -1115,9 +1270,6 @@ void GridMap::_notification(int p_what) {
_octant_exit_world(E.key);
}
- //_queue_octants_dirty(MAP_DIRTY_INSTANCES|MAP_DIRTY_TRANSFORMS);
- //_update_octants_callback();
- //_update_area_instances();
for (int i = 0; i < baked_meshes.size(); i++) {
RS::get_singleton()->instance_set_scenario(baked_meshes[i].instance, RID());
}
@@ -1145,6 +1297,15 @@ void GridMap::_update_visibility() {
for (int i = 0; i < baked_meshes.size(); i++) {
RS::get_singleton()->instance_set_visible(baked_meshes[i].instance, is_visible_in_tree());
}
+
+#ifdef DEBUG_ENABLED
+ for (const KeyValue &E : debug_octant_map) {
+ OctantDebug &octant_debug = *E.value;
+ if (octant_debug.debug_line_instance_rid.is_valid()) {
+ RS::get_singleton()->instance_set_visible(octant_debug.debug_line_instance_rid, is_visible_in_tree());
+ }
+ }
+#endif
}
void GridMap::_queue_octants_dirty() {
@@ -1158,12 +1319,17 @@ void GridMap::_queue_octants_dirty() {
void GridMap::_recreate_octant_data() {
recreating_octants = true;
+
HashMap cell_copy(cell_map);
_clear_internal();
for (const KeyValue &E : cell_copy) {
set_cell_item(Vector3i(E.key), E.value.item, E.value.rot);
}
+
recreating_octants = false;
+#ifdef DEBUG_ENABLED
+ _debug_update();
+#endif
}
void GridMap::_clear_internal() {
@@ -1178,6 +1344,10 @@ void GridMap::_clear_internal() {
octant_map.clear();
cell_map.clear();
+#ifdef DEBUG_ENABLED
+ _debug_clear_octants();
+ _debug_update();
+#endif
}
void GridMap::clear() {
@@ -1212,6 +1382,10 @@ void GridMap::_update_octants_callback() {
_update_visibility();
awaiting_update = false;
+
+#ifdef DEBUG_ENABLED
+ _debug_update_octants();
+#endif
}
void GridMap::_bind_methods() {
@@ -1302,6 +1476,12 @@ void GridMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("clear_baked_meshes"), &GridMap::clear_baked_meshes);
ClassDB::bind_method(D_METHOD("make_baked_meshes", "gen_lightmap_uv", "lightmap_uv_texel_size"), &GridMap::make_baked_meshes, DEFVAL(false), DEFVAL(0.1));
+ ClassDB::bind_method(D_METHOD("set_debug_show_octants", "enable"), &GridMap::set_debug_show_octants);
+ ClassDB::bind_method(D_METHOD("get_debug_show_octants"), &GridMap::get_debug_show_octants);
+
+ ClassDB::bind_method(D_METHOD("set_debug_octant_color", "color"), &GridMap::set_debug_octant_color);
+ ClassDB::bind_method(D_METHOD("get_debug_octant_color"), &GridMap::get_debug_octant_color);
+
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "mesh_library", PROPERTY_HINT_RESOURCE_TYPE, MeshLibrary::get_class_static()), "set_mesh_library", "get_mesh_library");
#ifndef PHYSICS_3D_DISABLED
ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material", PROPERTY_HINT_RESOURCE_TYPE, PhysicsMaterial::get_class_static()), "set_physics_material", "get_physics_material");
@@ -1322,6 +1502,9 @@ void GridMap::_bind_methods() {
#endif // PHYSICS_3D_DISABLED
ADD_GROUP("Navigation", "");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "bake_navigation"), "set_bake_navigation", "is_baking_navigation");
+ ADD_GROUP("Debug", "");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "debug_show_octants"), "set_debug_show_octants", "get_debug_show_octants");
+ ADD_PROPERTY(PropertyInfo(Variant::COLOR, "debug_octant_color"), "set_debug_octant_color", "get_debug_octant_color");
BIND_CONSTANT(INVALID_CELL_ITEM);
@@ -1707,6 +1890,21 @@ RID GridMap::get_bake_mesh_instance(int p_idx) {
return baked_meshes[p_idx].instance;
}
+void GridMap::set_debug_show_octants(bool p_enable) {
+#ifdef DEBUG_ENABLED
+ if (debug_show_octants == p_enable) {
+ return;
+ }
+
+ debug_show_octants = p_enable;
+ _debug_update();
+#endif
+}
+
+bool GridMap::get_debug_show_octants() const {
+ return debug_show_octants;
+}
+
GridMap::GridMap() {
set_notify_transform(true);
#if defined(DEBUG_ENABLED) && !defined(NAVIGATION_3D_DISABLED)
@@ -1902,10 +2100,16 @@ void GridMap::_navigation_map_changed(RID p_map) {
GridMap::~GridMap() {
clear();
-#if defined(DEBUG_ENABLED) && !defined(NAVIGATION_3D_DISABLED)
+
+#ifdef DEBUG_ENABLED
+ _debug_clear_octants();
+
+#ifndef NAVIGATION_3D_DISABLED
NavigationServer3D::get_singleton()->disconnect("map_changed", callable_mp(this, &GridMap::_navigation_map_changed));
NavigationServer3D::get_singleton()->disconnect("navigation_debug_changed", callable_mp(this, &GridMap::_update_navigation_debug_edge_connections));
-#endif // defined(DEBUG_ENABLED) && !defined(NAVIGATION_3D_DISABLED)
+#endif // NAVIGATION_3D_DISABLED
+
+#endif // DEBUG_ENABLED
}
#if defined(DEBUG_ENABLED) && !defined(NAVIGATION_3D_DISABLED)
diff --git a/modules/gridmap/grid_map.h b/modules/gridmap/grid_map.h
index 01e26fa894..4ad3cfc6b9 100644
--- a/modules/gridmap/grid_map.h
+++ b/modules/gridmap/grid_map.h
@@ -233,6 +233,27 @@ class GridMap : public Node3D {
Vector baked_meshes;
+ bool debug_show_octants = false;
+ Color debug_octant_color = Color(1.0, 1.0, 1.0, 1.0);
+#ifdef DEBUG_ENABLED
+ bool debug_dirty = false;
+ Ref debug_octant_line_material;
+
+ void _debug_update();
+ void _debug_update_octants();
+ void _debug_clear_octants();
+
+ RID debug_octant_line_mesh_rid;
+
+ struct OctantDebug {
+ RID debug_line_mesh_rid;
+ RID debug_line_instance_rid;
+ };
+ HashMap debug_octant_map;
+
+ Array _build_octant_line_mesh_arrays() const;
+#endif // DEBUG_ENABLED
+
protected:
bool _set(const StringName &p_name, const Variant &p_value);
bool _get(const StringName &p_name, Variant &r_ret) const;
@@ -341,6 +362,12 @@ public:
Array get_bake_meshes();
RID get_bake_mesh_instance(int p_idx);
+ void set_debug_show_octants(bool p_enable);
+ bool get_debug_show_octants() const;
+
+ void set_debug_octant_color(const Color &p_color);
+ Color get_debug_octant_color() const;
+
#ifndef NAVIGATION_3D_DISABLED
private:
static Callable _navmesh_source_geometry_parsing_callback;