From bef13de12f1e40123698ba35c87e00a25b68b459 Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Tue, 12 Apr 2022 16:19:06 -0400 Subject: [PATCH] Support mdlviewer on macOS Qt5 for macOS can actually use the same code as on Linux/Win, but apparently no one wants to build it with Vulkan support. Instead, we spawn a standalone SDL2 window. --- CMakeLists.txt | 5 +++++ README.md | 5 ++++- libxiv | 2 +- mdlviewer/include/mainwindow.h | 3 +++ mdlviewer/src/mainwindow.cpp | 32 +++++++++++++++++++++++++++-- renderer/CMakeLists.txt | 14 +++++++++++-- renderer/include/standalonewindow.h | 20 ++++++++++++++++++ renderer/src/renderer.cpp | 20 ++++++++++++++++-- renderer/src/standalonewindow.cpp | 20 ++++++++++++++++++ 9 files changed, 113 insertions(+), 8 deletions(-) create mode 100644 renderer/include/standalonewindow.h create mode 100644 renderer/src/standalonewindow.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d8f0f87..1c9c9ed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,6 +30,11 @@ else() set(LIBRARIES fmt::fmt ${LIBRARIES}) endif() +# macos +if(UNIX AND NOT LINUX) + set(USE_STANDALONE_WINDOW TRUE) +endif() + add_subdirectory(renderer) add_subdirectory(exdviewer) add_subdirectory(mdlviewer) \ No newline at end of file diff --git a/README.md b/README.md index 53a51fa..870b3ce 100644 --- a/README.md +++ b/README.md @@ -42,4 +42,7 @@ You must pass the path to your `sqpack` directory as the first argument. ### Note -The viewport uses Vulkan, so it must be supported on your system in order to work. \ No newline at end of file +The viewport uses Vulkan, so it must be supported on your system in order to work. + +If you're running mdlviewer on macOS (where Qt builds usually don't ship with MoltenVK unfortunatey) +mdlviewer will automatically reconfigure itself to use a standalone SDL2 window. \ No newline at end of file diff --git a/libxiv b/libxiv index 172e454..a7751a5 160000 --- a/libxiv +++ b/libxiv @@ -1 +1 @@ -Subproject commit 172e45412cf97c5e9df27710af0c5cc0cdfa9b75 +Subproject commit a7751a50bb71fdce2c3db9ba2d18f49f3d754300 diff --git a/mdlviewer/include/mainwindow.h b/mdlviewer/include/mainwindow.h index af3906f..1bce0dd 100644 --- a/mdlviewer/include/mainwindow.h +++ b/mdlviewer/include/mainwindow.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include "renderer.hpp" @@ -41,6 +42,7 @@ inline std::unordered_map slotToName = { class GameData; class VulkanWindow; +class StandaloneWindow; class MainWindow : public QMainWindow { public: @@ -58,4 +60,5 @@ private: Renderer* renderer; VulkanWindow* vkWindow; + StandaloneWindow* standaloneWindow; }; \ No newline at end of file diff --git a/mdlviewer/src/mainwindow.cpp b/mdlviewer/src/mainwindow.cpp index 3778733..1d01461 100644 --- a/mdlviewer/src/mainwindow.cpp +++ b/mdlviewer/src/mainwindow.cpp @@ -1,5 +1,7 @@ #include "mainwindow.h" +#include +#include #include #include #include @@ -8,12 +10,14 @@ #include #include #include +#include #include "gamedata.h" #include "exhparser.h" #include "exdparser.h" #include "mdlparser.h" +#ifndef USE_STANDALONE_WINDOW class VulkanWindow : public QWindow { public: @@ -62,6 +66,9 @@ private: Renderer* m_renderer; QVulkanInstance* m_instance; }; +#else +#include "standalonewindow.h" +#endif MainWindow::MainWindow(GameData& data) : data(data) { setWindowTitle("mdlviewer"); @@ -121,6 +128,10 @@ MainWindow::MainWindow(GameData& data) : data(data) { renderer = new Renderer(); + auto viewportLayout = new QVBoxLayout(); + layout->addLayout(viewportLayout); + +#ifndef USE_STANDALONE_WINDOW auto inst = new QVulkanInstance(); inst->setVkInstance(renderer->instance); inst->setFlags(QVulkanInstance::Flag::NoDebugOutputRedirect); @@ -131,9 +142,17 @@ MainWindow::MainWindow(GameData& data) : data(data) { auto widget = QWidget::createWindowContainer(vkWindow); - auto viewportLayout = new QVBoxLayout(); viewportLayout->addWidget(widget); - layout->addLayout(viewportLayout); +#else + standaloneWindow = new StandaloneWindow(renderer); + renderer->initSwapchain(standaloneWindow->getSurface(renderer->instance), 640, 480); + + QTimer* timer = new QTimer(); + connect(timer, &QTimer::timeout, this, [this] { + standaloneWindow->render(); + }); + timer->start(1000); +#endif QComboBox* raceCombo = new QComboBox(); for(auto [race, raceName] : raceNames) { @@ -158,7 +177,11 @@ MainWindow::MainWindow(GameData& data) : data(data) { } void MainWindow::refreshModel() { +#ifdef USE_STANDALONE_WINDOW + standaloneWindow->models.clear(); +#else vkWindow->models.clear(); +#endif for(auto gear : loadedGears) { QString modelID = QString("%1").arg(gear->modelInfo.primaryID, 4, 10, QLatin1Char('0')); @@ -167,6 +190,11 @@ void MainWindow::refreshModel() { resolvedModelPath = resolvedModelPath.arg(modelID, raceIDs[currentRace].data(), modelID, slotToName[gear->slot].data()); data.extractFile(resolvedModelPath.toStdString(), "top.mdl"); + +#ifndef USE_STANDALONE_WINDOW vkWindow->models.push_back(renderer->addModel(parseMDL("top.mdl"))); +#else + standaloneWindow->models.push_back(renderer->addModel(parseMDL("top.mdl"))); +#endif } } \ No newline at end of file diff --git a/renderer/CMakeLists.txt b/renderer/CMakeLists.txt index 1b768e3..23b6774 100644 --- a/renderer/CMakeLists.txt +++ b/renderer/CMakeLists.txt @@ -8,6 +8,16 @@ FetchContent_Declare( FetchContent_MakeAvailable(glm) -add_library(renderer src/renderer.cpp) +if(USE_STANDALONE_WINDOW) + find_package(SDL2 REQUIRED) + set(EXTRA_SRC src/standalonewindow.cpp) + set(EXTRA_LIBRARIES SDL2::SDL2) +endif() + +add_library(renderer src/renderer.cpp ${EXTRA_SRC}) target_include_directories(renderer PUBLIC include) -target_link_libraries(renderer PUBLIC Vulkan::Vulkan fmt::fmt libxiv glm::glm) \ No newline at end of file +target_link_libraries(renderer PUBLIC Vulkan::Vulkan fmt::fmt libxiv glm::glm ${EXTRA_LIBRARIES}) + +if(USE_STANDALONE_WINDOW) + target_compile_definitions(renderer PUBLIC USE_STANDALONE_WINDOW) +endif() \ No newline at end of file diff --git a/renderer/include/standalonewindow.h b/renderer/include/standalonewindow.h new file mode 100644 index 0000000..f3491df --- /dev/null +++ b/renderer/include/standalonewindow.h @@ -0,0 +1,20 @@ +#pragma once + +#include "renderer.hpp" + +struct SDL_Window; + +class StandaloneWindow { +public: + StandaloneWindow(Renderer* renderer); + + VkSurfaceKHR getSurface(VkInstance instance); + + void render(); + + std::vector models; + +private: + Renderer* m_renderer; + SDL_Window* m_window; +}; \ No newline at end of file diff --git a/renderer/src/renderer.cpp b/renderer/src/renderer.cpp index a6fb5e6..fd466ec 100644 --- a/renderer/src/renderer.cpp +++ b/renderer/src/renderer.cpp @@ -12,7 +12,23 @@ Renderer::Renderer() { VkApplicationInfo applicationInfo = {}; - const std::array instanceExtensions = {"VK_KHR_surface", "VK_KHR_xcb_surface", "VK_EXT_debug_utils", "VK_KHR_xlib_surface"}; + std::vector instanceExtensions = {"VK_EXT_debug_utils"}; + + uint32_t extensionCount = 0; + vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr); + + std::vector extensions(extensionCount); + vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.data()); + + for(auto& extension : extensions) { + if (strstr(extension.extensionName, "surface") != nullptr) { + instanceExtensions.push_back(extension.extensionName); + } + + if (strstr(extension.extensionName, "VK_KHR_get_physical_device_properties2") != nullptr) { + instanceExtensions.push_back(extension.extensionName); + } + } VkInstanceCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; @@ -35,7 +51,7 @@ Renderer::Renderer() { physicalDevice = devices[0]; - uint32_t extensionCount = 0; + extensionCount = 0; vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extensionCount, nullptr); diff --git a/renderer/src/standalonewindow.cpp b/renderer/src/standalonewindow.cpp new file mode 100644 index 0000000..943960a --- /dev/null +++ b/renderer/src/standalonewindow.cpp @@ -0,0 +1,20 @@ +#include "standalonewindow.h" + +#include +#include + +StandaloneWindow::StandaloneWindow(Renderer* renderer) : m_renderer(renderer) { + SDL_Init(SDL_INIT_EVERYTHING); + + m_window = SDL_CreateWindow("mdlviewer viewport", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 640, 480, SDL_WINDOW_VULKAN); +} + +VkSurfaceKHR StandaloneWindow::getSurface(VkInstance instance) { + VkSurfaceKHR surface = VK_NULL_HANDLE; + SDL_Vulkan_CreateSurface(m_window, instance, &surface); + return surface; +} + +void StandaloneWindow::render() { + m_renderer->render(models); +}