Added basic UI to VoxelInstancer to see how many instances exist

master
Marc Gilleron 2022-08-14 20:57:41 +01:00
parent 5c47188773
commit 54ffc240ba
6 changed files with 179 additions and 20 deletions

View File

@ -39,6 +39,7 @@ Godot 4 is required from this version.
- `VoxelGeneratorGraph`: Some nodes have default input connections, so it's no longer required to connect them manually to (X,Y,Z) inputs
- `VoxelGeneratorGraph`: Added minor optimization to share branches of nodes doing the same calculations
- `VoxelInstancer`: Added support for `VoxelTerrain`. This means only LOD0 works, but mesh-LODs should work.
- `VoxelInstancer`: Editor: added basic UI to see how many instances exist
- `VoxelLodTerrain`: exposed debug drawing options for development versions
- Smooth voxels

View File

@ -1,9 +1,33 @@
#include "voxel_instancer_editor_plugin.h"
#include "../../terrain/instancing/voxel_instancer.h"
#include "voxel_instancer_stat_view.h"
#include <editor/editor_scale.h>
#include <scene/gui/menu_button.h>
namespace zylann::voxel {
VoxelInstancerEditorPlugin::VoxelInstancerEditorPlugin() {}
namespace {
enum MenuItemID { //
MENU_SHOW_STATS
};
}
VoxelInstancerEditorPlugin::VoxelInstancerEditorPlugin() {
MenuButton *menu_button = memnew(MenuButton);
menu_button->set_text(VoxelInstancer::get_class_static());
{
menu_button->get_popup()->add_item("Show statistics", MENU_SHOW_STATS);
const int i = menu_button->get_popup()->get_item_index(MENU_SHOW_STATS);
menu_button->get_popup()->set_item_as_checkable(i, true);
menu_button->get_popup()->set_item_checked(i, false);
}
menu_button->get_popup()->connect(
"id_pressed", callable_mp(this, &VoxelInstancerEditorPlugin::_on_menu_item_selected));
menu_button->hide();
add_control_to_container(CONTAINER_SPATIAL_EDITOR_MENU, menu_button);
_menu_button = menu_button;
}
bool VoxelInstancerEditorPlugin::handles(Object *p_object) const {
ERR_FAIL_COND_V(p_object == nullptr, false);
@ -16,15 +40,60 @@ void VoxelInstancerEditorPlugin::edit(Object *p_object) {
instancer->debug_set_draw_enabled(true);
instancer->debug_set_draw_flag(VoxelInstancer::DEBUG_DRAW_ALL_BLOCKS, true);
_node = instancer;
if (_stat_view != nullptr) {
_stat_view->set_instancer(_node);
}
}
void VoxelInstancerEditorPlugin::make_visible(bool visible) {
_menu_button->set_visible(visible);
if (visible == false) {
if (_node != nullptr) {
_node->debug_set_draw_enabled(false);
_node = nullptr;
}
if (_stat_view != nullptr) {
_stat_view->hide();
_stat_view->set_instancer(nullptr);
}
} else {
if (_stat_view != nullptr) {
_stat_view->set_instancer(_node);
_stat_view->show();
}
}
}
void VoxelInstancerEditorPlugin::_on_menu_item_selected(int id) {
switch (id) {
case MENU_SHOW_STATS: {
const bool active = toggle_stat_view();
const int i = _menu_button->get_popup()->get_item_index(id);
_menu_button->get_popup()->set_item_checked(i, active);
} break;
}
}
bool VoxelInstancerEditorPlugin::toggle_stat_view() {
bool active = _stat_view == nullptr;
if (active) {
if (_stat_view == nullptr) {
_stat_view = memnew(VoxelInstancerStatView);
_stat_view->set_custom_minimum_size(Vector2(0, EDSCALE * 50));
add_control_to_container(CONTAINER_SPATIAL_EDITOR_BOTTOM, _stat_view);
}
_stat_view->set_instancer(_node);
_stat_view->show();
} else {
if (_stat_view != nullptr) {
remove_control_from_container(CONTAINER_SPATIAL_EDITOR_BOTTOM, _stat_view);
_stat_view->queue_delete();
_stat_view = nullptr;
}
}
return active;
}
} // namespace zylann::voxel

View File

@ -6,6 +6,7 @@
namespace zylann::voxel {
class VoxelInstancer;
class VoxelInstancerStatView;
class VoxelInstancerEditorPlugin : public EditorPlugin {
GDCLASS(VoxelInstancerEditorPlugin, EditorPlugin)
@ -17,7 +18,12 @@ public:
void make_visible(bool visible) override;
private:
bool toggle_stat_view();
void _on_menu_item_selected(int id);
MenuButton *_menu_button = nullptr;
VoxelInstancer *_node = nullptr;
VoxelInstancerStatView *_stat_view = nullptr;
};
} // namespace zylann::voxel

View File

@ -0,0 +1,76 @@
#ifndef VOXEL_INSTANCER_STAT_VIEW_H
#define VOXEL_INSTANCER_STAT_VIEW_H
#include "../../terrain/instancing/voxel_instancer.h"
#include <scene/gui/box_container.h>
#include <scene/gui/tree.h>
namespace zylann::voxel {
class VoxelInstancerStatView : public VBoxContainer {
GDCLASS(VoxelInstancerStatView, VBoxContainer)
public:
VoxelInstancerStatView() {
_tree = memnew(Tree);
_tree->set_columns(2);
_tree->set_select_mode(Tree::SELECT_ROW);
_tree->set_v_size_flags(Control::SIZE_EXPAND_FILL);
_tree->set_column_expand_ratio(0, 0.25f);
add_child(_tree);
}
void set_instancer(const VoxelInstancer *instancer) {
_instancer = instancer;
set_process(_instancer != nullptr);
}
private:
void _notification(int p_what) {
switch (p_what) {
case NOTIFICATION_PROCESS:
_process();
break;
}
}
void _process() {
ERR_FAIL_COND(_instancer == nullptr);
const VoxelInstancer &instancer = *_instancer;
Ref<VoxelInstanceLibrary> library = instancer.get_library();
instancer.debug_get_instance_counts(_count_per_layer);
_tree->clear();
if (library.is_null()) {
return;
}
TreeItem *root = _tree->create_item();
_tree->set_hide_root(true);
for (auto it = _count_per_layer.begin(); it != _count_per_layer.end(); ++it) {
TreeItem *tree_item = _tree->create_item(root);
const VoxelInstanceLibraryItem *lib_item = library->get_item_const(it->first);
ERR_CONTINUE(lib_item == nullptr);
String name = lib_item->get_item_name();
if (name == "") {
name = "[" + String::num_int64(it->first) + "]";
}
tree_item->set_text(0, name);
tree_item->set_text(1, String::num_int64(it->second));
}
}
Tree *_tree = nullptr;
const VoxelInstancer *_instancer = nullptr;
std::unordered_map<uint32_t, uint32_t> _count_per_layer;
};
} // namespace zylann::voxel
#endif // VOXEL_INSTANCER_STAT_VIEW_H

View File

@ -1605,28 +1605,33 @@ int VoxelInstancer::debug_get_block_count() const {
return _blocks.size();
}
Dictionary VoxelInstancer::debug_get_instance_counts() const {
Dictionary d;
void VoxelInstancer::debug_get_instance_counts(std::unordered_map<uint32_t, uint32_t> &counts_per_layer) const {
ZN_PROFILE_SCOPE();
counts_per_layer.clear();
for (auto it = _blocks.begin(); it != _blocks.end(); ++it) {
Block &block = **it;
if (!block.multimesh_instance.is_valid()) {
continue;
const Block &block = **it;
uint32_t count = block.scene_instances.size();
if (block.multimesh_instance.is_valid()) {
Ref<MultiMesh> multimesh = block.multimesh_instance.get_multimesh();
ZN_ASSERT_CONTINUE(multimesh.is_valid());
count += get_visible_instance_count(**multimesh);
}
Ref<MultiMesh> multimesh = block.multimesh_instance.get_multimesh();
ERR_FAIL_COND_V(multimesh.is_null(), Dictionary());
counts_per_layer[block.layer_id] += count;
}
}
const int count = get_visible_instance_count(**multimesh);
Variant *vptr = d.getptr(block.layer_id);
if (vptr == nullptr) {
d[block.layer_id] = count;
} else {
ERR_FAIL_COND_V(vptr->get_type() != Variant::INT, Dictionary());
*vptr = vptr->operator signed int() + count;
}
Dictionary VoxelInstancer::_b_debug_get_instance_counts() const {
Dictionary d;
std::unordered_map<uint32_t, uint32_t> map;
debug_get_instance_counts(map);
for (auto it = map.begin(); it != map.end(); ++it) {
d[it->first] = it->second;
}
return d;
}
@ -1773,7 +1778,7 @@ void VoxelInstancer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_up_mode"), &VoxelInstancer::get_up_mode);
ClassDB::bind_method(D_METHOD("debug_get_block_count"), &VoxelInstancer::debug_get_block_count);
ClassDB::bind_method(D_METHOD("debug_get_instance_counts"), &VoxelInstancer::debug_get_instance_counts);
ClassDB::bind_method(D_METHOD("debug_get_instance_counts"), &VoxelInstancer::_b_debug_get_instance_counts);
ClassDB::bind_method(D_METHOD("debug_dump_as_scene", "fpath"), &VoxelInstancer::debug_dump_as_scene);
ClassDB::bind_method(D_METHOD("debug_set_draw_enabled", "enabled"), &VoxelInstancer::debug_set_draw_enabled);
ClassDB::bind_method(D_METHOD("debug_is_draw_enabled"), &VoxelInstancer::debug_is_draw_enabled);

View File

@ -84,7 +84,7 @@ public:
// Debug
int debug_get_block_count() const;
Dictionary debug_get_instance_counts() const;
void debug_get_instance_counts(std::unordered_map<uint32_t, uint32_t> &counts_per_layer) const;
void debug_dump_as_scene(String fpath) const;
Node *debug_dump_as_nodes() const;
@ -161,6 +161,8 @@ private:
static void remove_floating_scene_instances(Block &block, const Transform3D &parent_transform, Box3i p_voxel_box,
const VoxelTool &voxel_tool, int block_size_po2);
Dictionary _b_debug_get_instance_counts() const;
static void _bind_methods();
// TODO Rename RenderBlock?