Merge pull request #118583 from YeldhamDev/gridmap_show_octants

Add octant visualization to `GridMap`
This commit is contained in:
Thaddeus Crews
2026-06-19 15:09:24 -05:00
3 changed files with 246 additions and 9 deletions
+213 -9
View File
@@ -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<OctantKey, Octant *> &ele : octant_map) {
OctantKey octant_key = ele.key;
HashMap<OctantKey, OctantDebug *, OctantKey>::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<OctantKey, Octant *> &ele : octant_map) {
OctantKey octant_key = ele.key;
HashMap<OctantKey, OctantDebug *, OctantKey>::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<Vector3> 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<OctantKey, Octant *> &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<OctantKey, OctantDebug *> &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<OctantKey, OctantDebug *> &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<IndexKey, Cell, IndexKey> cell_copy(cell_map);
_clear_internal();
for (const KeyValue<IndexKey, Cell> &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)