diff --git a/src/Framework.cpp b/src/Framework.cpp index dcc53ec6..64f0ae98 100644 --- a/src/Framework.cpp +++ b/src/Framework.cpp @@ -1179,14 +1179,122 @@ void Framework::draw_ui() { ImGui::Columns(1); // Mods: - draw_about(); + std::vector sidebar_entries{"About"}; + static int selected_sidebar_entry = 0; + static bool initialized_default_sidebar_entry = false; + + ImGui::BeginTable("UEVRTable", 2, ImGuiTableFlags_::ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_::ImGuiTableFlags_BordersOuterV | ImGuiTableFlags_::ImGuiTableFlags_SizingFixedFit); + ImGui::TableSetupColumn("UEVRLeftPane", ImGuiTableColumnFlags_WidthFixed, 150.0f); + ImGui::TableSetupColumn("UEVRRightPane", ImGuiTableColumnFlags_WidthStretch); + + ImGui::TableNextRow(); + ImGui::TableSetColumnIndex(0); // Set to the first column + + ImGui::BeginChild("UEVRLeftPane", ImVec2(0, 0), true); + auto dcs = [&](const char* label, uint32_t page_value) -> bool { + ImGui::PushStyleVar(ImGuiStyleVar_SelectableTextAlign, ImVec2(0.5f, 0.5f)); + if (ImGui::Selectable(label, selected_sidebar_entry == page_value)) { + selected_sidebar_entry = page_value; + ImGui::PopStyleVar(); + return true; + } + ImGui::PopStyleVar(); + return false; + }; + + dcs("About", 0); if (m_error.empty() && m_game_data_initialized) { - m_mods->on_draw_ui(); + struct Info { + size_t mn{}; + size_t mx{}; + std::shared_ptr mod{}; + bool has_sidebar_entries{}; + }; + + std::vector mod_sidebar_ranges{}; + + for (auto& mod : m_mods->get_mods()) { + auto entries = mod->get_sidebar_entries(); + + if (!entries.empty()) { + mod_sidebar_ranges.push_back(Info{sidebar_entries.size(), sidebar_entries.size() + entries.size(), mod, true}); + sidebar_entries.insert(sidebar_entries.end(), entries.begin(), entries.end()); + } else { + mod_sidebar_ranges.push_back(Info{sidebar_entries.size(), sidebar_entries.size() + 1, mod, false}); + sidebar_entries.push_back(mod->get_name().data()); + } + } + + for (size_t i = 1; i < sidebar_entries.size(); ++i) { + for (const auto& range : mod_sidebar_ranges) { + if (i == range.mn) { + // Set first entry as default ("Runtime" entry of VR mod) + if (range.has_sidebar_entries && !initialized_default_sidebar_entry) { + selected_sidebar_entry = i; + initialized_default_sidebar_entry = true; + } + + ImGui::Text(range.mod->get_name().data()); + } + } + + dcs(sidebar_entries[i].c_str(), i); + } + + if (selected_sidebar_entry >= sidebar_entries.size()) { + selected_sidebar_entry = 0; + } + + ImGui::EndChild(); + ImGui::TableNextColumn(); // Move to the next column (right) + + if (selected_sidebar_entry > 0) { + // Find the mod that owns this entry + for (const auto& range : mod_sidebar_ranges) { + if (selected_sidebar_entry >= range.mn && selected_sidebar_entry < range.mx) { + if (range.has_sidebar_entries) { + range.mod->on_draw_sidebar_entry(sidebar_entries[selected_sidebar_entry]); + } else { + range.mod->on_draw_ui(); + } + + break; + } + } + } else { + draw_about(); + } + + /*for (auto& mod : m_mods->get_mods()) { + mod->on_draw_ui(); + }*/ + + //m_mods->on_draw_ui(); + + ImGui::EndTable(); } else if (!m_game_data_initialized) { + ImGui::EndChild(); + + if (selected_sidebar_entry == 0) { + ImGui::TableNextColumn(); + draw_about(); + } + + ImGui::EndTable(); + ImGui::TextWrapped("Framework is currently initializing..."); ImGui::TextWrapped("This menu will close after initialization if you have the remember option enabled."); } else if (!m_error.empty()) { + ImGui::EndChild(); + + if (selected_sidebar_entry == 0) { + ImGui::TableNextColumn(); + draw_about(); + } + + ImGui::EndTable(); + ImGui::TextWrapped("Framework error: %s", m_error.c_str()); } @@ -1213,12 +1321,6 @@ void Framework::draw_ui() { } void Framework::draw_about() { - if (!ImGui::CollapsingHeader("About")) { - return; - } - - ImGui::TreePush("About"); - ImGui::Text("Author: praydog"); ImGui::Text("Unreal Engine VR"); ImGui::Text("https://github.com/praydog/UEVR"); @@ -1232,7 +1334,7 @@ void Framework::draw_about() { std::string text; }; - static std::array licenses{ + static std::array licenses{ License{ "glm", license::glm }, License{ "imgui", license::imgui }, License{ "safetyhook", license::safetyhook }, @@ -1240,7 +1342,8 @@ void Framework::draw_about() { License{ "json", license::json }, License{ "bddisasm", utility::narrow(license::bddisasm) }, License{ "directxtk", license::directxtk }, - License{ "directxtk12", license::directxtk } + License{ "directxtk12", license::directxtk }, + License{ "openvr", license::openvr }, }; for (const auto& license : licenses) { @@ -1253,8 +1356,6 @@ void Framework::draw_about() { } ImGui::Separator(); - - ImGui::TreePop(); } void Framework::set_imgui_style() noexcept { diff --git a/src/Mod.hpp b/src/Mod.hpp index 95932f36..c4d09a15 100644 --- a/src/Mod.hpp +++ b/src/Mod.hpp @@ -380,6 +380,8 @@ class Mod { virtual std::optional on_initialize() { return std::nullopt; }; virtual std::optional on_initialize_d3d_thread() { return std::nullopt; }; + virtual std::vector get_sidebar_entries() { return {}; }; + // This gets called after updating stuff like keyboard/mouse input to imgui // can be used to override these inputs e.g. with a custom input system // like VR controllers @@ -389,6 +391,7 @@ class Mod { virtual void on_post_frame() {}; // after imgui rendering is done virtual void on_post_present() {}; // actually after present gets called virtual void on_draw_ui() {}; + virtual void on_draw_sidebar_entry(std::string_view in_entry) {}; virtual void on_device_reset() {}; virtual bool on_message(HWND wnd, UINT message, WPARAM w_param, LPARAM l_param) { return true; }; virtual void on_xinput_get_state(uint32_t* retval, uint32_t user_index, XINPUT_STATE* state) {}; diff --git a/src/mods/FrameworkConfig.cpp b/src/mods/FrameworkConfig.cpp index bd409243..560615ea 100644 --- a/src/mods/FrameworkConfig.cpp +++ b/src/mods/FrameworkConfig.cpp @@ -12,12 +12,6 @@ std::optional FrameworkConfig::on_initialize() { } void FrameworkConfig::on_draw_ui() { - if (!ImGui::CollapsingHeader("Configuration")) { - return; - } - - ImGui::TreePush("Configuration"); - m_menu_key->draw("Menu Key"); m_show_cursor_key->draw("Show Cursor Key"); m_remember_menu_state->draw("Remember Menu Open/Closed State"); @@ -27,8 +21,6 @@ void FrameworkConfig::on_draw_ui() { if (m_font_size->draw("Font Size")) { g_framework->set_font_size(m_font_size->value()); } - - ImGui::TreePop(); } void FrameworkConfig::on_frame() { diff --git a/src/mods/PluginLoader.cpp b/src/mods/PluginLoader.cpp index 9a1a32a5..fa862cd7 100644 --- a/src/mods/PluginLoader.cpp +++ b/src/mods/PluginLoader.cpp @@ -914,44 +914,40 @@ void PluginLoader::reload_plugins() { } void PluginLoader::on_draw_ui() { - ImGui::SetNextItemOpen(false, ImGuiCond_Once); - - if (ImGui::CollapsingHeader(get_name().data())) { - std::scoped_lock _{m_mux}; + std::scoped_lock _{m_mux}; - if (ImGui::Button("Attempt Unload Plugins")) { - attempt_unload_plugins(); - } + if (ImGui::Button("Attempt Unload Plugins")) { + attempt_unload_plugins(); + } - if (ImGui::Button("Reload Plugins")) { - attempt_unload_plugins(); - reload_plugins(); - } + if (ImGui::Button("Reload Plugins")) { + attempt_unload_plugins(); + reload_plugins(); + } - if (!m_plugins.empty()) { - ImGui::Text("Loaded plugins:"); + if (!m_plugins.empty()) { + ImGui::Text("Loaded plugins:"); - for (auto&& [name, _] : m_plugins) { - ImGui::Text(name.c_str()); - } - } else { - ImGui::Text("No plugins loaded."); + for (auto&& [name, _] : m_plugins) { + ImGui::Text(name.c_str()); } + } else { + ImGui::Text("No plugins loaded."); + } - if (!m_plugin_load_errors.empty()) { - ImGui::Spacing(); - ImGui::Text("Errors:"); - for (auto&& [name, error] : m_plugin_load_errors) { - ImGui::Text("%s - %s", name.c_str(), error.c_str()); - } + if (!m_plugin_load_errors.empty()) { + ImGui::Spacing(); + ImGui::Text("Errors:"); + for (auto&& [name, error] : m_plugin_load_errors) { + ImGui::Text("%s - %s", name.c_str(), error.c_str()); } + } - if (!m_plugin_load_warnings.empty()) { - ImGui::Spacing(); - ImGui::Text("Warnings:"); - for (auto&& [name, warning] : m_plugin_load_warnings) { - ImGui::Text("%s - %s", name.c_str(), warning.c_str()); - } + if (!m_plugin_load_warnings.empty()) { + ImGui::Spacing(); + ImGui::Text("Warnings:"); + for (auto&& [name, warning] : m_plugin_load_warnings) { + ImGui::Text("%s - %s", name.c_str(), warning.c_str()); } } } diff --git a/src/mods/UObjectHook.cpp b/src/mods/UObjectHook.cpp index 8b65b0fb..2e8209ea 100644 --- a/src/mods/UObjectHook.cpp +++ b/src/mods/UObjectHook.cpp @@ -787,383 +787,381 @@ void UObjectHook::destroy_overlapper() { std::future> sorting_task{}; void UObjectHook::on_draw_ui() { - if (ImGui::CollapsingHeader("UObjectHook")) { - activate(); + activate(); - if (!m_fully_hooked) { - ImGui::Text("Waiting for UObjectBase to be hooked..."); - return; - } + if (!m_fully_hooked) { + ImGui::Text("Waiting for UObjectBase to be hooked..."); + return; + } - std::shared_lock _{m_mutex}; + std::shared_lock _{m_mutex}; - if (!m_motion_controller_attached_components.empty()) { - ImGui::SetNextItemOpen(true, ImGuiCond_Once); - const auto made = ImGui::TreeNode("Attached Components"); + if (!m_motion_controller_attached_components.empty()) { + ImGui::SetNextItemOpen(true, ImGuiCond_Once); + const auto made = ImGui::TreeNode("Attached Components"); - if (made) { - if (ImGui::Button("Detach all")) { - m_motion_controller_attached_components.clear(); - } + if (made) { + if (ImGui::Button("Detach all")) { + m_motion_controller_attached_components.clear(); + } - // make a copy because the user could press the detach button while iterating - auto attached = m_motion_controller_attached_components; + // make a copy because the user could press the detach button while iterating + auto attached = m_motion_controller_attached_components; - for (auto& it : attached) { - if (!this->exists_unsafe(it.first) || it.second == nullptr) { - continue; - } + for (auto& it : attached) { + if (!this->exists_unsafe(it.first) || it.second == nullptr) { + continue; + } - auto comp = it.first; - std::wstring comp_name = comp->get_class()->get_fname().to_string() + L" " + comp->get_fname().to_string(); + auto comp = it.first; + std::wstring comp_name = comp->get_class()->get_fname().to_string() + L" " + comp->get_fname().to_string(); - if (ImGui::TreeNode(utility::narrow(comp_name).data())) { - ui_handle_object(comp); + if (ImGui::TreeNode(utility::narrow(comp_name).data())) { + ui_handle_object(comp); - ImGui::TreePop(); - } + ImGui::TreePop(); } - - ImGui::TreePop(); } + + ImGui::TreePop(); } + } - if (m_overlap_detection_actor == nullptr) { - if (ImGui::Button("Spawn Overlapper")) { - spawn_overlapper(0); - spawn_overlapper(1); - } - } else if (!this->exists_unsafe(m_overlap_detection_actor)) { - m_overlap_detection_actor = nullptr; - } else { - ImGui::SetNextItemOpen(true, ImGuiCond_Once); - const auto made = ImGui::TreeNode("Overlapped Objects"); + if (m_overlap_detection_actor == nullptr) { + if (ImGui::Button("Spawn Overlapper")) { + spawn_overlapper(0); + spawn_overlapper(1); + } + } else if (!this->exists_unsafe(m_overlap_detection_actor)) { + m_overlap_detection_actor = nullptr; + } else { + ImGui::SetNextItemOpen(true, ImGuiCond_Once); + const auto made = ImGui::TreeNode("Overlapped Objects"); - if (made) { - if (ImGui::Button("Destroy Overlapper")) { - destroy_overlapper(); - } + if (made) { + if (ImGui::Button("Destroy Overlapper")) { + destroy_overlapper(); + } - ImGui::SameLine(); - bool attach_all = false; - if (ImGui::Button("Attach all")) { - attach_all = true; - } + ImGui::SameLine(); + bool attach_all = false; + if (ImGui::Button("Attach all")) { + attach_all = true; + } - auto overlapped_components = m_overlap_detection_actor->get_overlapping_components(); + auto overlapped_components = m_overlap_detection_actor->get_overlapping_components(); - for (auto& it : overlapped_components) { - auto comp = (sdk::USceneComponent*)it; - if (!this->exists_unsafe(comp)) { - continue; - } + for (auto& it : overlapped_components) { + auto comp = (sdk::USceneComponent*)it; + if (!this->exists_unsafe(comp)) { + continue; + } - if (m_spawned_spheres.contains(comp) && m_spawned_spheres_to_components.contains(comp)) { - comp = m_spawned_spheres_to_components[comp]; - } + if (m_spawned_spheres.contains(comp) && m_spawned_spheres_to_components.contains(comp)) { + comp = m_spawned_spheres_to_components[comp]; + } - if (attach_all){ - if (!m_motion_controller_attached_components.contains(comp)) { - m_motion_controller_attached_components[comp] = std::make_shared(); - } + if (attach_all){ + if (!m_motion_controller_attached_components.contains(comp)) { + m_motion_controller_attached_components[comp] = std::make_shared(); } + } - std::wstring comp_name = comp->get_class()->get_fname().to_string() + L" " + comp->get_fname().to_string(); + std::wstring comp_name = comp->get_class()->get_fname().to_string() + L" " + comp->get_fname().to_string(); - if (ImGui::TreeNode(utility::narrow(comp_name).data())) { - ui_handle_object(comp); - ImGui::TreePop(); - } + if (ImGui::TreeNode(utility::narrow(comp_name).data())) { + ui_handle_object(comp); + ImGui::TreePop(); } - - ImGui::TreePop(); } - } - ImGui::Text("Objects: %zu (%zu actual)", m_objects.size(), sdk::FUObjectArray::get()->get_object_count()); + ImGui::TreePop(); + } + } - if (ImGui::TreeNode("Recent Objects")) { - for (auto& object : m_most_recent_objects) { - if (!this->exists_unsafe(object)) { - continue; - } + ImGui::Text("Objects: %zu (%zu actual)", m_objects.size(), sdk::FUObjectArray::get()->get_object_count()); - if (ImGui::TreeNode(utility::narrow(object->get_full_name()).data())) { - ui_handle_object(object); - ImGui::TreePop(); - } + if (ImGui::TreeNode("Recent Objects")) { + for (auto& object : m_most_recent_objects) { + if (!this->exists_unsafe(object)) { + continue; } - ImGui::TreePop(); + if (ImGui::TreeNode(utility::narrow(object->get_full_name()).data())) { + ui_handle_object(object); + ImGui::TreePop(); + } } - // Display common objects like things related to the player - if (ImGui::TreeNode("Common Objects")) { - auto world = sdk::UGameEngine::get()->get_world(); + ImGui::TreePop(); + } - if (world != nullptr) { - if (ImGui::TreeNode("PlayerController")) { - auto player_controller = sdk::UGameplayStatics::get()->get_player_controller(world, 0); + // Display common objects like things related to the player + if (ImGui::TreeNode("Common Objects")) { + auto world = sdk::UGameEngine::get()->get_world(); - if (player_controller != nullptr) { - ui_handle_object(player_controller); - } else { - ImGui::Text("No player controller"); - } + if (world != nullptr) { + if (ImGui::TreeNode("PlayerController")) { + auto player_controller = sdk::UGameplayStatics::get()->get_player_controller(world, 0); - ImGui::TreePop(); + if (player_controller != nullptr) { + ui_handle_object(player_controller); + } else { + ImGui::Text("No player controller"); } - if (ImGui::TreeNode("Acknowledged Pawn")) { - auto player_controller = sdk::UGameplayStatics::get()->get_player_controller(world, 0); + ImGui::TreePop(); + } - if (player_controller != nullptr) { - auto pawn = player_controller->get_acknowledged_pawn(); + if (ImGui::TreeNode("Acknowledged Pawn")) { + auto player_controller = sdk::UGameplayStatics::get()->get_player_controller(world, 0); - if (pawn != nullptr) { - ui_handle_object(pawn); - } else { - ImGui::Text("No pawn"); - } + if (player_controller != nullptr) { + auto pawn = player_controller->get_acknowledged_pawn(); + + if (pawn != nullptr) { + ui_handle_object(pawn); } else { - ImGui::Text("No player controller"); + ImGui::Text("No pawn"); } - - ImGui::TreePop(); + } else { + ImGui::Text("No player controller"); } - if (ImGui::TreeNode("Camera Manager")) { - auto player_controller = sdk::UGameplayStatics::get()->get_player_controller(world, 0); + ImGui::TreePop(); + } + + if (ImGui::TreeNode("Camera Manager")) { + auto player_controller = sdk::UGameplayStatics::get()->get_player_controller(world, 0); - if (player_controller != nullptr) { - auto camera_manager = player_controller->get_player_camera_manager(); + if (player_controller != nullptr) { + auto camera_manager = player_controller->get_player_camera_manager(); - if (camera_manager != nullptr) { - ui_handle_object((sdk::UObject*)camera_manager); - } else { - ImGui::Text("No camera manager"); - } + if (camera_manager != nullptr) { + ui_handle_object((sdk::UObject*)camera_manager); } else { - ImGui::Text("No player controller"); + ImGui::Text("No camera manager"); } - - ImGui::TreePop(); + } else { + ImGui::Text("No player controller"); } - if (ImGui::TreeNode("World")) { - ui_handle_object(world); - ImGui::TreePop(); - } - } else { - ImGui::Text("No world"); + ImGui::TreePop(); } - ImGui::TreePop(); + if (ImGui::TreeNode("World")) { + ui_handle_object(world); + ImGui::TreePop(); + } + } else { + ImGui::Text("No world"); } - if (ImGui::TreeNode("Objects by class")) { - static char filter[256]{}; - ImGui::InputText("Filter", filter, sizeof(filter)); + ImGui::TreePop(); + } - const bool filter_empty = std::string_view{filter}.empty(); + if (ImGui::TreeNode("Objects by class")) { + static char filter[256]{}; + ImGui::InputText("Filter", filter, sizeof(filter)); - const auto now = std::chrono::steady_clock::now(); - bool needs_sort = true; + const bool filter_empty = std::string_view{filter}.empty(); - if (sorting_task.valid()) { - // Check if the sorting task is finished - if (sorting_task.wait_for(std::chrono::seconds(0)) == std::future_status::ready) { - // Do something if needed when sorting is done - m_sorted_classes = sorting_task.get(); - needs_sort = true; - } else { - needs_sort = false; - } - } else { + const auto now = std::chrono::steady_clock::now(); + bool needs_sort = true; + + if (sorting_task.valid()) { + // Check if the sorting task is finished + if (sorting_task.wait_for(std::chrono::seconds(0)) == std::future_status::ready) { + // Do something if needed when sorting is done + m_sorted_classes = sorting_task.get(); needs_sort = true; + } else { + needs_sort = false; } + } else { + needs_sort = true; + } - if (needs_sort) { - auto sort_classes = [this](std::vector classes) { - std::sort(classes.begin(), classes.end(), [this](sdk::UClass* a, sdk::UClass* b) { - std::shared_lock _{m_mutex}; - if (!m_objects.contains(a) || !m_objects.contains(b)) { - return false; - } - - return m_meta_objects[a]->full_name < m_meta_objects[b]->full_name; - }); + if (needs_sort) { + auto sort_classes = [this](std::vector classes) { + std::sort(classes.begin(), classes.end(), [this](sdk::UClass* a, sdk::UClass* b) { + std::shared_lock _{m_mutex}; + if (!m_objects.contains(a) || !m_objects.contains(b)) { + return false; + } - return classes; - }; + return m_meta_objects[a]->full_name < m_meta_objects[b]->full_name; + }); - auto unsorted_classes = std::vector{}; + return classes; + }; - for (auto& [c, set]: m_objects_by_class) { - unsorted_classes.push_back(c); - } + auto unsorted_classes = std::vector{}; - // Launch sorting in a separate thread - sorting_task = std::async(std::launch::async, sort_classes, unsorted_classes); - m_last_sort_time = now; + for (auto& [c, set]: m_objects_by_class) { + unsorted_classes.push_back(c); } - const auto wide_filter = utility::widen(filter); + // Launch sorting in a separate thread + sorting_task = std::async(std::launch::async, sort_classes, unsorted_classes); + m_last_sort_time = now; + } - bool made_child = ImGui::BeginChild("Objects by class entries", ImVec2(0, 0), true, ImGuiWindowFlags_::ImGuiWindowFlags_HorizontalScrollbar); + const auto wide_filter = utility::widen(filter); - utility::ScopeGuard sg{[made_child]() { - if (made_child) { - ImGui::EndChild(); - } - }}; + bool made_child = ImGui::BeginChild("Objects by class entries", ImVec2(0, 0), true, ImGuiWindowFlags_::ImGuiWindowFlags_HorizontalScrollbar); - for (auto uclass : m_sorted_classes) { - const auto& objects_ref = m_objects_by_class[uclass]; + utility::ScopeGuard sg{[made_child]() { + if (made_child) { + ImGui::EndChild(); + } + }}; - if (objects_ref.empty()) { - continue; - } + for (auto uclass : m_sorted_classes) { + const auto& objects_ref = m_objects_by_class[uclass]; - if (!m_meta_objects.contains(uclass)) { - continue; - } + if (objects_ref.empty()) { + continue; + } - const auto uclass_name = utility::narrow(m_meta_objects[uclass]->full_name); - bool valid = true; + if (!m_meta_objects.contains(uclass)) { + continue; + } - if (!filter_empty) { - valid = false; + const auto uclass_name = utility::narrow(m_meta_objects[uclass]->full_name); + bool valid = true; - for (auto super = (sdk::UStruct*)uclass; super; super = super->get_super_struct()) { - if (auto it = m_meta_objects.find(super); it != m_meta_objects.end()) { - if (it->second->full_name.find(wide_filter) != std::wstring::npos) { - valid = true; - break; - } + if (!filter_empty) { + valid = false; + + for (auto super = (sdk::UStruct*)uclass; super; super = super->get_super_struct()) { + if (auto it = m_meta_objects.find(super); it != m_meta_objects.end()) { + if (it->second->full_name.find(wide_filter) != std::wstring::npos) { + valid = true; + break; } } } + } - if (!valid) { - continue; - } - - if (ImGui::TreeNode(uclass_name.data())) { - std::vector objects{}; - - for (auto object : objects_ref) { - objects.push_back(object); - } - - std::sort(objects.begin(), objects.end(), [this](sdk::UObjectBase* a, sdk::UObjectBase* b) { - return m_meta_objects[a]->full_name < m_meta_objects[b]->full_name; - }); + if (!valid) { + continue; + } - if (uclass->is_a(sdk::AActor::static_class())) { - static char component_add_name[256]{}; + if (ImGui::TreeNode(uclass_name.data())) { + std::vector objects{}; - if (ImGui::InputText("Add Component Permanently", component_add_name, sizeof(component_add_name), ImGuiInputTextFlags_::ImGuiInputTextFlags_EnterReturnsTrue)) { - const auto component_c = sdk::find_uobject(utility::widen(component_add_name)); + for (auto object : objects_ref) { + objects.push_back(object); + } - if (component_c != nullptr) { - m_on_creation_add_component_jobs[uclass] = [this, component_c](sdk::UObject* object) { - if (!this->exists(object)) { - return; - } + std::sort(objects.begin(), objects.end(), [this](sdk::UObjectBase* a, sdk::UObjectBase* b) { + return m_meta_objects[a]->full_name < m_meta_objects[b]->full_name; + }); - if (object == object->get_class()->get_class_default_object()) { - return; - } + if (uclass->is_a(sdk::AActor::static_class())) { + static char component_add_name[256]{}; - auto actor = (sdk::AActor*)object; - auto component = (sdk::UObject*)actor->add_component_by_class(component_c); + if (ImGui::InputText("Add Component Permanently", component_add_name, sizeof(component_add_name), ImGuiInputTextFlags_::ImGuiInputTextFlags_EnterReturnsTrue)) { + const auto component_c = sdk::find_uobject(utility::widen(component_add_name)); - if (component != nullptr) { - if (component->get_class()->is_a(sdk::find_uobject(L"Class /Script/Engine.SphereComponent"))) { - struct SphereRadiusParams { - float radius{}; - }; + if (component_c != nullptr) { + m_on_creation_add_component_jobs[uclass] = [this, component_c](sdk::UObject* object) { + if (!this->exists(object)) { + return; + } - auto params = SphereRadiusParams{}; - params.radius = 100.f; + if (object == object->get_class()->get_class_default_object()) { + return; + } - const auto fn = component->get_class()->find_function(L"SetSphereRadius"); + auto actor = (sdk::AActor*)object; + auto component = (sdk::UObject*)actor->add_component_by_class(component_c); - if (fn != nullptr) { - component->process_event(fn, ¶ms); - } - } + if (component != nullptr) { + if (component->get_class()->is_a(sdk::find_uobject(L"Class /Script/Engine.SphereComponent"))) { + struct SphereRadiusParams { + float radius{}; + }; - struct { - bool hidden{false}; - bool propagate{true}; - } set_hidden_params{}; + auto params = SphereRadiusParams{}; + params.radius = 100.f; - const auto fn = component->get_class()->find_function(L"SetHiddenInGame"); + const auto fn = component->get_class()->find_function(L"SetSphereRadius"); if (fn != nullptr) { - component->process_event(fn, &set_hidden_params); + component->process_event(fn, ¶ms); } + } - actor->finish_add_component(component); + struct { + bool hidden{false}; + bool propagate{true}; + } set_hidden_params{}; - // Set component_add_name to empty - component_add_name[0] = '\0'; - } else { - component_add_name[0] = 'e'; - component_add_name[1] = 'r'; - component_add_name[2] = 'r'; - component_add_name[3] = '\0'; + const auto fn = component->get_class()->find_function(L"SetHiddenInGame"); + + if (fn != nullptr) { + component->process_event(fn, &set_hidden_params); } - }; - } else { - strcpy_s(component_add_name, "Nonexistent component"); - } - } - } - for (const auto& object : objects) { - const auto made = ImGui::TreeNode(utility::narrow(m_meta_objects[object]->full_name).data()); - // make right click context - if (ImGui::BeginPopupContextItem()) { - auto sc = [](const std::string& text) { - if (OpenClipboard(NULL)) { - EmptyClipboard(); - HGLOBAL hcd = GlobalAlloc(GMEM_DDESHARE, text.size() + 1); - char* data = (char*)GlobalLock(hcd); - strcpy(data, text.c_str()); - GlobalUnlock(hcd); - SetClipboardData(CF_TEXT, hcd); - CloseClipboard(); + actor->finish_add_component(component); + + // Set component_add_name to empty + component_add_name[0] = '\0'; + } else { + component_add_name[0] = 'e'; + component_add_name[1] = 'r'; + component_add_name[2] = 'r'; + component_add_name[3] = '\0'; } }; + } else { + strcpy_s(component_add_name, "Nonexistent component"); + } + } + } - if (ImGui::Button("Copy Name")) { - sc(utility::narrow(m_meta_objects[object]->full_name)); - } - - if (ImGui::Button("Copy Address")) { - const auto hex = (std::stringstream{} << std::hex << (uintptr_t)object).str(); - sc(hex); + for (const auto& object : objects) { + const auto made = ImGui::TreeNode(utility::narrow(m_meta_objects[object]->full_name).data()); + // make right click context + if (ImGui::BeginPopupContextItem()) { + auto sc = [](const std::string& text) { + if (OpenClipboard(NULL)) { + EmptyClipboard(); + HGLOBAL hcd = GlobalAlloc(GMEM_DDESHARE, text.size() + 1); + char* data = (char*)GlobalLock(hcd); + strcpy(data, text.c_str()); + GlobalUnlock(hcd); + SetClipboardData(CF_TEXT, hcd); + CloseClipboard(); } + }; - ImGui::EndPopup(); + if (ImGui::Button("Copy Name")) { + sc(utility::narrow(m_meta_objects[object]->full_name)); } - if (made) { - ui_handle_object((sdk::UObject*)object); - - ImGui::TreePop(); + if (ImGui::Button("Copy Address")) { + const auto hex = (std::stringstream{} << std::hex << (uintptr_t)object).str(); + sc(hex); } + + ImGui::EndPopup(); } - ImGui::TreePop(); + if (made) { + ui_handle_object((sdk::UObject*)object); + + ImGui::TreePop(); + } } - } - ImGui::TreePop(); + ImGui::TreePop(); + } } + + ImGui::TreePop(); } } diff --git a/src/mods/UObjectHook.hpp b/src/mods/UObjectHook.hpp index c9cf96e8..41f63723 100644 --- a/src/mods/UObjectHook.hpp +++ b/src/mods/UObjectHook.hpp @@ -42,6 +42,8 @@ class UObjectHook : public Mod { void activate(); protected: + std::string_view get_name() const override { return "UObjectHook"; }; + void on_pre_engine_tick(sdk::UGameEngine* engine, float delta) override; void on_draw_ui() override; diff --git a/src/mods/VR.cpp b/src/mods/VR.cpp index a689adee..2da00f93 100644 --- a/src/mods/VR.cpp +++ b/src/mods/VR.cpp @@ -1854,81 +1854,11 @@ uint32_t VR::get_hmd_height() const { return get_runtime()->get_height(); } -void VR::on_draw_ui() { - ZoneScopedN(__FUNCTION__); - - // create VR tree entry in menu (imgui) - ImGui::PushID("VR"); - ImGui::SetNextItemOpen(true, ImGuiCond_::ImGuiCond_Once); - if (!m_fake_stereo_hook->has_attempted_to_hook_engine() || !m_fake_stereo_hook->has_attempted_to_hook_slate()) { - std::string adjusted_name = get_name().data(); - adjusted_name += " (Loading...)"; - - if (!ImGui::CollapsingHeader(adjusted_name.data())) { - ImGui::PopID(); - return; - } - } else { - if (!ImGui::CollapsingHeader(get_name().data())) { - ImGui::PopID(); - return; - } - } - ImGui::PopID(); +void VR::on_draw_sidebar_entry(std::string_view name) { + const auto hash = utility::hash(name.data()); - auto display_error = [](auto& runtime, std::string dll_name) { - if (runtime == nullptr || !runtime->error && runtime->loaded) { - return; - } - - if (runtime->error && runtime->dll_missing) { - ImGui::TextWrapped("%s not loaded: %s not found", runtime->name().data(), dll_name.data()); - ImGui::TextWrapped("Please select %s from the loader if you want to use %s", runtime->name().data(), runtime->name().data()); - } else if (runtime->error) { - ImGui::TextWrapped("%s not loaded: %s", runtime->name().data(), runtime->error->c_str()); - } else { - ImGui::TextWrapped("%s not loaded: Unknown error", runtime->name().data()); - } - - ImGui::Separator(); - }; - - if (!get_runtime()->loaded || get_runtime()->error) { - display_error(m_openxr, "openxr_loader.dll"); - display_error(m_openvr, "openvr_api.dll"); - } - - if (!get_runtime()->loaded) { - ImGui::TextWrapped("No runtime loaded."); - - if (ImGui::Button("Attempt to reinitialize")) { - clean_initialize(); - } - - return; - } - - if (ImGui::Button("Set Standing Height")) { - m_standing_origin.y = get_position(0).y; - } - - ImGui::SameLine(); - - if (ImGui::Button("Set Standing Origin")) { - m_standing_origin = get_position(0); - } - - ImGui::SameLine(); - - if (ImGui::Button("Recenter View")) { - recenter_view(); - } - - ImGui::SameLine(); - - if (ImGui::Button("Reinitialize Runtime")) { - get_runtime()->wants_reinitialize = true; - } + // Draw the ui thats always drawn first. + on_draw_ui(); const auto made_child = ImGui::BeginChild("VRChild", ImVec2(0, 0), true, ImGuiWindowFlags_::ImGuiWindowFlags_NavFlattened); @@ -1948,9 +1878,9 @@ void VR::on_draw_ui() { PAGE_DEBUG, }; - static SelectedPage selected_page = PAGE_RUNTIME; + SelectedPage selected_page = PAGE_RUNTIME; - ImGui::BeginTable("VRTable", 2, ImGuiTableFlags_::ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_::ImGuiTableFlags_BordersOuterV | ImGuiTableFlags_::ImGuiTableFlags_SizingFixedFit); + /*ImGui::BeginTable("VRTable", 2, ImGuiTableFlags_::ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_::ImGuiTableFlags_BordersOuterV | ImGuiTableFlags_::ImGuiTableFlags_SizingFixedFit); ImGui::TableSetupColumn("LeftPane", ImGuiTableColumnFlags_WidthFixed, 150.0f); ImGui::TableSetupColumn("RightPane", ImGuiTableColumnFlags_WidthStretch); @@ -1985,7 +1915,35 @@ void VR::on_draw_ui() { } ImGui::TableNextColumn(); // Move to the next column (right) - ImGui::BeginGroup(); + ImGui::BeginGroup();*/ + + switch (hash) { + case "Runtime"_fnv: + selected_page = PAGE_RUNTIME; + break; + case "Unreal"_fnv: + selected_page = PAGE_UNREAL; + break; + case "Input"_fnv: + selected_page = PAGE_INPUT; + break; + case "Camera"_fnv: + selected_page = PAGE_CAMERA; + break; + case "Console/CVars"_fnv: + selected_page = PAGE_CONSOLE; + break; + case "Compatibility"_fnv: + selected_page = PAGE_COMPATIBILITY; + break; + case "Debug"_fnv: + selected_page = PAGE_DEBUG; + break; + default: + ImGui::Text("Unknown page selected"); + break; + } + if (selected_page == PAGE_RUNTIME) { if (m_has_hw_scheduling) { ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1.0f, 0.0f, 0.0f, 1.0f)); @@ -2170,7 +2128,84 @@ void VR::on_draw_ui() { } ImGui::EndGroup(); - ImGui::EndTable(); + //ImGui::EndTable(); +} + +void VR::on_draw_ui() { + ZoneScopedN(__FUNCTION__); + + // create VR tree entry in menu (imgui) + ImGui::PushID("VR"); + ImGui::SetNextItemOpen(true, ImGuiCond_::ImGuiCond_Once); + if (!m_fake_stereo_hook->has_attempted_to_hook_engine() || !m_fake_stereo_hook->has_attempted_to_hook_slate()) { + std::string adjusted_name = get_name().data(); + adjusted_name += " (Loading...)"; + + if (!ImGui::CollapsingHeader(adjusted_name.data())) { + ImGui::PopID(); + return; + } + } else { + /*if (!ImGui::CollapsingHeader(get_name().data())) { + ImGui::PopID(); + return; + }*/ + } + ImGui::PopID(); + + auto display_error = [](auto& runtime, std::string dll_name) { + if (runtime == nullptr || !runtime->error && runtime->loaded) { + return; + } + + if (runtime->error && runtime->dll_missing) { + ImGui::TextWrapped("%s not loaded: %s not found", runtime->name().data(), dll_name.data()); + ImGui::TextWrapped("Please select %s from the loader if you want to use %s", runtime->name().data(), runtime->name().data()); + } else if (runtime->error) { + ImGui::TextWrapped("%s not loaded: %s", runtime->name().data(), runtime->error->c_str()); + } else { + ImGui::TextWrapped("%s not loaded: Unknown error", runtime->name().data()); + } + + ImGui::Separator(); + }; + + if (!get_runtime()->loaded || get_runtime()->error) { + display_error(m_openxr, "openxr_loader.dll"); + display_error(m_openvr, "openvr_api.dll"); + } + + if (!get_runtime()->loaded) { + ImGui::TextWrapped("No runtime loaded."); + + if (ImGui::Button("Attempt to reinitialize")) { + clean_initialize(); + } + + return; + } + + if (ImGui::Button("Set Standing Height")) { + m_standing_origin.y = get_position(0).y; + } + + ImGui::SameLine(); + + if (ImGui::Button("Set Standing Origin")) { + m_standing_origin = get_position(0); + } + + ImGui::SameLine(); + + if (ImGui::Button("Recenter View")) { + recenter_view(); + } + + ImGui::SameLine(); + + if (ImGui::Button("Reinitialize Runtime")) { + get_runtime()->wants_reinitialize = true; + } } Vector4f VR::get_position(uint32_t index, bool grip) const { diff --git a/src/mods/VR.hpp b/src/mods/VR.hpp index d86d4d13..e03021e1 100644 --- a/src/mods/VR.hpp +++ b/src/mods/VR.hpp @@ -80,10 +80,23 @@ class VR : public Mod { return clean_initialize(); } + std::vector get_sidebar_entries() override { + return { + "Runtime", + "Unreal", + "Input", + "Camera", + "Console/CVars", + "Compatibility", + "Debug" + }; + } + void on_config_load(const utility::Config& cfg, bool set_defaults) override; void on_config_save(utility::Config& cfg) override; void on_draw_ui() override; + void on_draw_sidebar_entry(std::string_view name) override; void on_pre_imgui_frame() override; void on_frame() override; void on_present() override;