From bdf0f78e0745be0fe572ac53540b4a75f988dd8d Mon Sep 17 00:00:00 2001 From: lawnjelly Date: Sun, 11 Feb 2024 08:45:16 +0000 Subject: [PATCH] Editor 3D view mesh stats Similar to information window, add a small optional window to display face count and other stats. --- editor/plugins/spatial_editor_plugin.cpp | 120 ++++++++++++++++++++++- editor/plugins/spatial_editor_plugin.h | 2 + scene/resources/mesh.cpp | 49 +++++++++ scene/resources/mesh.h | 19 ++++ 4 files changed, 186 insertions(+), 4 deletions(-) diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp index 63cf8e6040b..cb02b42241a 100644 --- a/editor/plugins/spatial_editor_plugin.cpp +++ b/editor/plugins/spatial_editor_plugin.cpp @@ -47,6 +47,7 @@ #include "scene/3d/collision_shape.h" #include "scene/3d/lod_manager.h" #include "scene/3d/mesh_instance.h" +#include "scene/3d/multimesh_instance.h" #include "scene/3d/physics_body.h" #include "scene/3d/room_manager.h" #include "scene/3d/visual_instance.h" @@ -2779,6 +2780,19 @@ void SpatialEditorViewport::_notification(int p_what) { _update_camera(delta); + bool show_selected_info = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_SELECTED_INFO)) && (message_time <= 0); + + struct SelectedInfo { + bool filled = false; + uint32_t tri_count = 0; + uint32_t vertex_count = 0; + uint32_t index_count = 0; + uint32_t mesh_count = 0; + uint32_t multimesh_count = 0; + uint32_t surface_count = 0; + uint32_t array_format = 0; + } selected_info; + Map &selection = editor_selection->get_selection(); bool changed = false; @@ -2790,6 +2804,48 @@ void SpatialEditorViewport::_notification(int p_what) { continue; } + // Only retrieve stats for selected items if we are currently showing the selection stats box. + if (show_selected_info) { + MeshInstance *mi = Object::cast_to(sp); + if (mi) { + const Ref &mesh = mi->get_mesh(); + if (mesh.is_valid()) { + selected_info.filled = true; + const Mesh::CachedStats &stats = mesh->get_cached_stats(); + selected_info.tri_count += stats.triangle_count; + selected_info.vertex_count += stats.vertex_count; + selected_info.index_count += stats.index_count; + selected_info.mesh_count += 1; + selected_info.surface_count += mesh->get_surface_count(); + selected_info.array_format |= stats.array_format; + } + } + + MultiMeshInstance *mmi = Object::cast_to(sp); + if (mmi) { + const Ref &mm = mmi->get_multimesh(); + if (mm.is_valid()) { + const Ref &mesh = mm->get_mesh(); + int icount = mm->get_visible_instance_count(); + if (icount < 0) { + icount = mm->get_instance_count(); + } + + if (mesh.is_valid() && icount) { + selected_info.filled = true; + const Mesh::CachedStats &stats = mesh->get_cached_stats(); + selected_info.tri_count += stats.triangle_count * icount; + selected_info.vertex_count += stats.vertex_count * icount; + selected_info.index_count += stats.index_count * icount; + selected_info.mesh_count += icount; + selected_info.multimesh_count += 1; + selected_info.surface_count += mesh->get_surface_count() * icount; + selected_info.array_format |= stats.array_format; + } + } + } + } + SpatialEditorSelectedItem *se = editor_selection->get_node_editor_data(sp); if (!se) { continue; @@ -2841,11 +2897,17 @@ void SpatialEditorViewport::_notification(int p_what) { if (message != last_message) { surface->update(); last_message = message; - } - message_time -= get_physics_process_delta_time(); - if (message_time < 0) { - surface->update(); + // If there is now no message, + // disable the timing counter. + if (message == "") { + message_time = 0; + } + } else { + message_time -= get_physics_process_delta_time(); + if (message_time < 0) { + surface->update(); + } } } @@ -2893,6 +2955,29 @@ void SpatialEditorViewport::_notification(int p_what) { info_label->set_text(text); } + selected_info_label->set_visible(show_selected_info && selected_info.filled); + if (show_selected_info) { + if (selected_info.filled) { + String text; + if (selected_info.multimesh_count > 0) { + text += TTR("MultiMeshes:") + " " + itos(selected_info.multimesh_count) + "\n"; + } + if (selected_info.mesh_count > 1) { + text += TTR("Meshes:") + " " + itos(selected_info.mesh_count) + "\n"; + } + if (selected_info.surface_count > 1) { + text += TTR("Surfaces:") + " " + itos(selected_info.surface_count) + "\n"; + } + text += TTR("Triangles:") + " " + itos(selected_info.tri_count) + "\n"; + text += TTR("Vertices:") + " " + itos(selected_info.vertex_count) + "\n"; + text += TTR("Indices:") + " " + itos(selected_info.index_count); + + selected_info_label->set_text(text); + } else { + selected_info_label->set_text(""); + } + } + // FPS Counter. bool show_fps = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_FPS)); fps_label->set_visible(show_fps); @@ -2959,6 +3044,7 @@ void SpatialEditorViewport::_notification(int p_what) { preview_camera->add_style_override("disabled", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles")); info_label->add_style_override("normal", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles")); + selected_info_label->add_style_override("normal", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles")); fps_label->add_style_override("normal", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles")); cinema_label->add_style_override("normal", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles")); locked_label->add_style_override("normal", editor->get_gui_base()->get_stylebox("Information3dViewport", "EditorStyles")); @@ -3374,6 +3460,12 @@ void SpatialEditorViewport::_menu_option(int p_option) { bool current = view_menu->get_popup()->is_item_checked(idx); view_menu->get_popup()->set_item_checked(idx, !current); + } break; + case VIEW_SELECTED_INFO: { + int idx = view_menu->get_popup()->get_item_index(VIEW_SELECTED_INFO); + bool current = view_menu->get_popup()->is_item_checked(idx); + view_menu->get_popup()->set_item_checked(idx, !current); + } break; case VIEW_FPS: { int idx = view_menu->get_popup()->get_item_index(VIEW_FPS); @@ -3744,6 +3836,14 @@ void SpatialEditorViewport::set_state(const Dictionary &p_state) { _menu_option(VIEW_INFORMATION); } } + if (p_state.has("selected_info")) { + bool selected_info = p_state["selected_info"]; + + int idx = view_menu->get_popup()->get_item_index(VIEW_SELECTED_INFO); + if (view_menu->get_popup()->is_item_checked(idx) != selected_info) { + _menu_option(VIEW_SELECTED_INFO); + } + } if (p_state.has("fps")) { bool fps = p_state["fps"]; @@ -3806,6 +3906,7 @@ Dictionary SpatialEditorViewport::get_state() const { d["doppler"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_AUDIO_DOPPLER)); d["gizmos"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_GIZMOS)); d["information"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_INFORMATION)); + d["selected_info"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_SELECTED_INFO)); d["fps"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_FPS)); d["half_res"] = viewport_container->get_stretch_shrink() > 1; d["cinematic_preview"] = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(VIEW_CINEMATIC_PREVIEW)); @@ -4344,6 +4445,7 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_gizmos", TTR("View Gizmos")), VIEW_GIZMOS); view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(VIEW_GIZMOS), true); view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_information", TTR("View Information")), VIEW_INFORMATION); + view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_selected_info", TTR("View Selected Mesh Stats")), VIEW_SELECTED_INFO); view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_fps", TTR("View FPS")), VIEW_FPS); view_menu->get_popup()->add_separator(); view_menu->get_popup()->add_check_shortcut(ED_SHORTCUT("spatial_editor/view_half_resolution", TTR("Half Resolution")), VIEW_HALF_RESOLUTION); @@ -4428,6 +4530,16 @@ SpatialEditorViewport::SpatialEditorViewport(SpatialEditor *p_spatial_editor, Ed surface->add_child(info_label); info_label->hide(); + selected_info_label = memnew(Label); + selected_info_label->set_anchor_and_margin(MARGIN_LEFT, ANCHOR_BEGIN, 10 * EDSCALE); + selected_info_label->set_anchor_and_margin(MARGIN_TOP, ANCHOR_END, -20 * EDSCALE); + selected_info_label->set_anchor_and_margin(MARGIN_RIGHT, ANCHOR_BEGIN, 90 * EDSCALE); + selected_info_label->set_anchor_and_margin(MARGIN_BOTTOM, ANCHOR_END, -10 * EDSCALE); + selected_info_label->set_h_grow_direction(GROW_DIRECTION_END); + selected_info_label->set_v_grow_direction(GROW_DIRECTION_BEGIN); + surface->add_child(selected_info_label); + selected_info_label->hide(); + cinema_label = memnew(Label); cinema_label->set_anchor_and_margin(MARGIN_TOP, ANCHOR_BEGIN, 10 * EDSCALE); cinema_label->set_h_grow_direction(GROW_DIRECTION_END); diff --git a/editor/plugins/spatial_editor_plugin.h b/editor/plugins/spatial_editor_plugin.h index 475aa43fb95..3f0587a09be 100644 --- a/editor/plugins/spatial_editor_plugin.h +++ b/editor/plugins/spatial_editor_plugin.h @@ -204,6 +204,7 @@ class SpatialEditorViewport : public Control { VIEW_AUDIO_DOPPLER, VIEW_GIZMOS, VIEW_INFORMATION, + VIEW_SELECTED_INFO, VIEW_FPS, VIEW_DISPLAY_NORMAL, VIEW_DISPLAY_WIREFRAME, @@ -283,6 +284,7 @@ private: Vector2 previous_mouse_position; Label *info_label; + Label *selected_info_label; Label *cinema_label; Label *locked_label; Label *zoom_limit_label; diff --git a/scene/resources/mesh.cpp b/scene/resources/mesh.cpp index 729e8d10569..c792c230763 100644 --- a/scene/resources/mesh.cpp +++ b/scene/resources/mesh.cpp @@ -42,6 +42,52 @@ Mesh::ConvexDecompositionFunc Mesh::convex_decomposition_function = nullptr; +#ifdef TOOLS_ENABLED +const Mesh::CachedStats &Mesh::get_cached_stats() const { + if (_cached_stats.dirty) { + _cached_stats.dirty = false; + + _cached_stats.triangle_count = get_triangle_count(); + + // Vertex count. + _cached_stats.vertex_count = 0; + for (int i = 0; i < get_surface_count(); i++) { + _cached_stats.vertex_count += surface_get_array_len(i); + } + + // Index count. + _cached_stats.index_count = 0; + for (int i = 0; i < get_surface_count(); i++) { + _cached_stats.index_count += surface_get_index_count(i); + } + + // Array format. + _cached_stats.array_format = 0; + for (int i = 0; i < get_surface_count(); i++) { + _cached_stats.array_format |= surface_get_format(i); + } + } + + return _cached_stats; +} +#endif + +int Mesh::surface_get_index_count(int p_idx) const { + ERR_FAIL_INDEX_V(p_idx, get_surface_count(), 0); + + switch (surface_get_primitive_type(p_idx)) { + case PRIMITIVE_TRIANGLES: + case PRIMITIVE_TRIANGLE_FAN: + case PRIMITIVE_TRIANGLE_STRIP: { + return (surface_get_format(p_idx) & ARRAY_FORMAT_INDEX) ? surface_get_array_index_len(p_idx) : surface_get_array_len(p_idx); + } break; + default: { + } break; + } + + return 0; +} + int Mesh::surface_get_triangle_count(int p_idx) const { ERR_FAIL_INDEX_V(p_idx, get_surface_count(), 0); @@ -625,6 +671,9 @@ void Mesh::set_storage_mode(StorageMode p_storage_mode) { void Mesh::clear_cache() const { triangle_mesh.unref(); debug_lines.clear(); +#ifdef TOOLS_ENABLED + _cached_stats.dirty = true; +#endif } Vector> Mesh::convex_decompose(int p_max_convex_hulls) const { diff --git a/scene/resources/mesh.h b/scene/resources/mesh.h index d9c973eb5d6..3f5e5b33b89 100644 --- a/scene/resources/mesh.h +++ b/scene/resources/mesh.h @@ -126,6 +126,14 @@ public: STORAGE_MODE_CPU_AND_GPU, }; + struct CachedStats { + bool dirty = true; + uint32_t triangle_count = 0; + uint32_t vertex_count = 0; + uint32_t index_count = 0; + uint32_t array_format = 0; + }; + virtual int get_surface_count() const = 0; virtual int surface_get_array_len(int p_idx) const = 0; virtual int surface_get_array_index_len(int p_idx) const = 0; @@ -138,11 +146,15 @@ public: virtual Ref surface_get_material(int p_idx) const = 0; virtual int get_blend_shape_count() const = 0; int surface_get_triangle_count(int p_idx) const; + int surface_get_index_count(int p_idx) const; virtual StringName get_blend_shape_name(int p_index) const = 0; virtual void set_blend_shape_name(int p_index, const StringName &p_name) = 0; int get_triangle_count() const; PoolVector get_faces() const; +#ifdef TOOLS_ENABLED + const CachedStats &get_cached_stats() const; +#endif Ref generate_triangle_mesh() const; Ref generate_triangle_mesh_from_aabb() const; void generate_debug_mesh_lines(Vector &r_lines); @@ -167,6 +179,13 @@ public: Vector> convex_decompose(int p_max_convex_hulls = -1) const; Mesh(); + +private: +#ifdef TOOLS_ENABLED + // Only for use in the editor. + // No need to bloat exports. + mutable CachedStats _cached_stats; +#endif }; class ArrayMesh : public Mesh {