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})
|
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)
|
|
@ -43,3 +43,6 @@ 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
2
libxiv
|
@ -1 +1 @@
|
||||||
Subproject commit 172e45412cf97c5e9df27710af0c5cc0cdfa9b75
|
Subproject commit a7751a50bb71fdce2c3db9ba2d18f49f3d754300
|
|
@ -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;
|
||||||
};
|
};
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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()
|
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() {
|
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);
|
||||||
|
|
||||||
|
|
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