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:
parent
813e8fddd9
commit
bef13de12f
9 changed files with 113 additions and 8 deletions
|
@ -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)
|
|
@ -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.
|
||||
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
2
libxiv
|
@ -1 +1 @@
|
|||
Subproject commit 172e45412cf97c5e9df27710af0c5cc0cdfa9b75
|
||||
Subproject commit a7751a50bb71fdce2c3db9ba2d18f49f3d754300
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "renderer.hpp"
|
||||
|
||||
|
@ -41,6 +42,7 @@ inline std::unordered_map<Slot, std::string_view> slotToName = {
|
|||
|
||||
class GameData;
|
||||
class VulkanWindow;
|
||||
class StandaloneWindow;
|
||||
|
||||
class MainWindow : public QMainWindow {
|
||||
public:
|
||||
|
@ -58,4 +60,5 @@ private:
|
|||
|
||||
Renderer* renderer;
|
||||
VulkanWindow* vkWindow;
|
||||
StandaloneWindow* standaloneWindow;
|
||||
};
|
|
@ -1,5 +1,7 @@
|
|||
#include "mainwindow.h"
|
||||
|
||||
#include <QWindow>
|
||||
#include <QVulkanInstance>
|
||||
#include <QHBoxLayout>
|
||||
#include <QTableWidget>
|
||||
#include <fmt/core.h>
|
||||
|
@ -8,12 +10,14 @@
|
|||
#include <QLineEdit>
|
||||
#include <QResizeEvent>
|
||||
#include <QComboBox>
|
||||
#include <QTimer>
|
||||
|
||||
#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
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
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()
|
20
renderer/include/standalonewindow.h
Normal file
20
renderer/include/standalonewindow.h
Normal 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;
|
||||
};
|
|
@ -12,7 +12,23 @@
|
|||
Renderer::Renderer() {
|
||||
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 = {};
|
||||
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);
|
||||
|
||||
|
|
20
renderer/src/standalonewindow.cpp
Normal file
20
renderer/src/standalonewindow.cpp
Normal 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);
|
||||
}
|
Loading…
Add table
Reference in a new issue