1
Fork 0
mirror of https://github.com/redstrate/Novus.git synced 2025-04-25 05:17:44 +00:00

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.
This commit is contained in:
Joshua Goins 2022-04-12 16:19:06 -04:00
parent 813e8fddd9
commit bef13de12f
9 changed files with 113 additions and 8 deletions

View file

@ -30,6 +30,11 @@ else()
set(LIBRARIES fmt::fmt ${LIBRARIES}) set(LIBRARIES fmt::fmt ${LIBRARIES})
endif() endif()
# macos
if(UNIX AND NOT LINUX)
set(USE_STANDALONE_WINDOW TRUE)
endif()
add_subdirectory(renderer) add_subdirectory(renderer)
add_subdirectory(exdviewer) add_subdirectory(exdviewer)
add_subdirectory(mdlviewer) add_subdirectory(mdlviewer)

View file

@ -42,4 +42,7 @@ You must pass the path to your `sqpack` directory as the first argument.
### Note ### Note
The viewport uses Vulkan, so it must be supported on your system in order to work. 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.

2
libxiv

@ -1 +1 @@
Subproject commit 172e45412cf97c5e9df27710af0c5cc0cdfa9b75 Subproject commit a7751a50bb71fdce2c3db9ba2d18f49f3d754300

View file

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <QMainWindow> #include <QMainWindow>
#include <unordered_map>
#include "renderer.hpp" #include "renderer.hpp"
@ -41,6 +42,7 @@ inline std::unordered_map<Slot, std::string_view> slotToName = {
class GameData; class GameData;
class VulkanWindow; class VulkanWindow;
class StandaloneWindow;
class MainWindow : public QMainWindow { class MainWindow : public QMainWindow {
public: public:
@ -58,4 +60,5 @@ private:
Renderer* renderer; Renderer* renderer;
VulkanWindow* vkWindow; VulkanWindow* vkWindow;
StandaloneWindow* standaloneWindow;
}; };

View file

@ -1,5 +1,7 @@
#include "mainwindow.h" #include "mainwindow.h"
#include <QWindow>
#include <QVulkanInstance>
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QTableWidget> #include <QTableWidget>
#include <fmt/core.h> #include <fmt/core.h>
@ -8,12 +10,14 @@
#include <QLineEdit> #include <QLineEdit>
#include <QResizeEvent> #include <QResizeEvent>
#include <QComboBox> #include <QComboBox>
#include <QTimer>
#include "gamedata.h" #include "gamedata.h"
#include "exhparser.h" #include "exhparser.h"
#include "exdparser.h" #include "exdparser.h"
#include "mdlparser.h" #include "mdlparser.h"
#ifndef USE_STANDALONE_WINDOW
class VulkanWindow : public QWindow class VulkanWindow : public QWindow
{ {
public: public:
@ -62,6 +66,9 @@ private:
Renderer* m_renderer; Renderer* m_renderer;
QVulkanInstance* m_instance; QVulkanInstance* m_instance;
}; };
#else
#include "standalonewindow.h"
#endif
MainWindow::MainWindow(GameData& data) : data(data) { MainWindow::MainWindow(GameData& data) : data(data) {
setWindowTitle("mdlviewer"); setWindowTitle("mdlviewer");
@ -121,6 +128,10 @@ MainWindow::MainWindow(GameData& data) : data(data) {
renderer = new Renderer(); renderer = new Renderer();
auto viewportLayout = new QVBoxLayout();
layout->addLayout(viewportLayout);
#ifndef USE_STANDALONE_WINDOW
auto inst = new QVulkanInstance(); auto inst = new QVulkanInstance();
inst->setVkInstance(renderer->instance); inst->setVkInstance(renderer->instance);
inst->setFlags(QVulkanInstance::Flag::NoDebugOutputRedirect); inst->setFlags(QVulkanInstance::Flag::NoDebugOutputRedirect);
@ -131,9 +142,17 @@ MainWindow::MainWindow(GameData& data) : data(data) {
auto widget = QWidget::createWindowContainer(vkWindow); auto widget = QWidget::createWindowContainer(vkWindow);
auto viewportLayout = new QVBoxLayout();
viewportLayout->addWidget(widget); 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(); QComboBox* raceCombo = new QComboBox();
for(auto [race, raceName] : raceNames) { for(auto [race, raceName] : raceNames) {
@ -158,7 +177,11 @@ MainWindow::MainWindow(GameData& data) : data(data) {
} }
void MainWindow::refreshModel() { void MainWindow::refreshModel() {
#ifdef USE_STANDALONE_WINDOW
standaloneWindow->models.clear();
#else
vkWindow->models.clear(); vkWindow->models.clear();
#endif
for(auto gear : loadedGears) { for(auto gear : loadedGears) {
QString modelID = QString("%1").arg(gear->modelInfo.primaryID, 4, 10, QLatin1Char('0')); 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()); resolvedModelPath = resolvedModelPath.arg(modelID, raceIDs[currentRace].data(), modelID, slotToName[gear->slot].data());
data.extractFile(resolvedModelPath.toStdString(), "top.mdl"); data.extractFile(resolvedModelPath.toStdString(), "top.mdl");
#ifndef USE_STANDALONE_WINDOW
vkWindow->models.push_back(renderer->addModel(parseMDL("top.mdl"))); vkWindow->models.push_back(renderer->addModel(parseMDL("top.mdl")));
#else
standaloneWindow->models.push_back(renderer->addModel(parseMDL("top.mdl")));
#endif
} }
} }

View file

@ -8,6 +8,16 @@ FetchContent_Declare(
FetchContent_MakeAvailable(glm) 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_include_directories(renderer PUBLIC include)
target_link_libraries(renderer PUBLIC Vulkan::Vulkan fmt::fmt libxiv glm::glm) 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()

View file

@ -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<RenderModel> models;
private:
Renderer* m_renderer;
SDL_Window* m_window;
};

View file

@ -12,7 +12,23 @@
Renderer::Renderer() { Renderer::Renderer() {
VkApplicationInfo applicationInfo = {}; VkApplicationInfo applicationInfo = {};
const std::array<const char*, 4> instanceExtensions = {"VK_KHR_surface", "VK_KHR_xcb_surface", "VK_EXT_debug_utils", "VK_KHR_xlib_surface"}; std::vector<const char*> instanceExtensions = {"VK_EXT_debug_utils"};
uint32_t extensionCount = 0;
vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr);
std::vector<VkExtensionProperties> 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 = {}; VkInstanceCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
@ -35,7 +51,7 @@ Renderer::Renderer() {
physicalDevice = devices[0]; physicalDevice = devices[0];
uint32_t extensionCount = 0; extensionCount = 0;
vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr,
&extensionCount, nullptr); &extensionCount, nullptr);

View file

@ -0,0 +1,20 @@
#include "standalonewindow.h"
#include <SDL.h>
#include <SDL_vulkan.h>
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);
}