From 51ea3c3920cb2da32be978ba46e6b2548f92f6b3 Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Sat, 27 Apr 2024 13:45:08 -0400 Subject: [PATCH] Create reusable material part This takes the existing properties widget in the new material editor and makes it reusable across multiple applications. It's now added to the Armoury which shows the gear's used materials. It's also added to the Data Explorer which now supports viewing material files. I fixed the render viewport crashing when hiding it again, and made it even more resilient. --- armoury/CMakeLists.txt | 3 +- armoury/include/mainwindow.h | 4 +- armoury/include/materialview.h | 22 -- armoury/include/singlegearview.h | 3 + armoury/src/mainwindow.cpp | 17 +- armoury/src/materialview.cpp | 16 -- armoury/src/singlegearview.cpp | 15 ++ common/include/filetypes.h | 2 +- common/src/filetypes.cpp | 9 +- mateditor/CMakeLists.txt | 5 +- mateditor/src/mainwindow.cpp | 7 +- parts/CMakeLists.txt | 1 + parts/mdl/mdlpart.cpp | 6 + parts/mdl/mdlpart.h | 2 + parts/mdl/vulkanwindow.cpp | 26 +- parts/mdl/vulkanwindow.h | 1 + parts/mtrl/CMakeLists.txt | 20 ++ .../mtrl/knownvalues.h | 219 ----------------- parts/mtrl/mtrlpart.cpp | 224 ++++++++++++++++++ .../mtrl/mtrlpart.h | 8 +- renderer/src/rendermanager.cpp | 9 +- sagasu/CMakeLists.txt | 1 + sagasu/src/indexer.cpp | 1 + sagasu/src/mainwindow.cpp | 6 + 24 files changed, 342 insertions(+), 285 deletions(-) delete mode 100644 armoury/include/materialview.h delete mode 100644 armoury/src/materialview.cpp create mode 100644 parts/mtrl/CMakeLists.txt rename mateditor/src/materialpropertyedit.cpp => parts/mtrl/knownvalues.h (66%) create mode 100644 parts/mtrl/mtrlpart.cpp rename mateditor/include/materialpropertyedit.h => parts/mtrl/mtrlpart.h (79%) diff --git a/armoury/CMakeLists.txt b/armoury/CMakeLists.txt index b36e9c9..2d77610 100644 --- a/armoury/CMakeLists.txt +++ b/armoury/CMakeLists.txt @@ -11,7 +11,6 @@ target_sources(novus-armoury include/gearlistwidget.h include/gearview.h include/mainwindow.h - include/materialview.h include/metadataview.h include/penumbraapi.h include/settingswindow.h @@ -25,7 +24,6 @@ target_sources(novus-armoury src/gearview.cpp src/main.cpp src/mainwindow.cpp - src/materialview.cpp src/metadataview.cpp src/penumbraapi.cpp src/settingswindow.cpp @@ -41,6 +39,7 @@ target_link_libraries(novus-armoury Novus::MdlPart Novus::CmpPart Novus::SklbPart + Novus::MtrlPart Physis::Physis Physis::Logger imgui diff --git a/armoury/include/mainwindow.h b/armoury/include/mainwindow.h index f9ce4b3..ac16e31 100644 --- a/armoury/include/mainwindow.h +++ b/armoury/include/mainwindow.h @@ -9,8 +9,8 @@ #include "fullmodelviewer.h" #include "gearview.h" -#include "materialview.h" #include "metadataview.h" +#include "mtrlpart.h" #include "novusmainwindow.h" #include "singlegearview.h" @@ -31,7 +31,7 @@ protected: private: SingleGearView *gearView = nullptr; FullModelViewer *fullModelViewer = nullptr; - MaterialView *materialView = nullptr; + QTabWidget *materialsView = nullptr; MetadataView *metadataView = nullptr; GameData &data; diff --git a/armoury/include/materialview.h b/armoury/include/materialview.h deleted file mode 100644 index 70b750e..0000000 --- a/armoury/include/materialview.h +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Joshua Goins -// SPDX-License-Identifier: GPL-3.0-or-later - -#pragma once - -#include "filecache.h" -#include "gearview.h" - -#include - -struct GameData; - -class MaterialView : public QWidget -{ - Q_OBJECT - -public: - explicit MaterialView(GameData *data, QWidget *parent = nullptr); - -private: - GameData *data = nullptr; -}; \ No newline at end of file diff --git a/armoury/include/singlegearview.h b/armoury/include/singlegearview.h index 320937d..60c5fa1 100644 --- a/armoury/include/singlegearview.h +++ b/armoury/include/singlegearview.h @@ -18,6 +18,7 @@ public: explicit SingleGearView(GameData *data, FileCache &cache, QWidget *parent = nullptr); QString getLoadedGearPath() const; + QList getLoadedMaterials() const; Q_SIGNALS: void gearChanged(); @@ -31,6 +32,8 @@ Q_SIGNALS: void addToFullModelViewer(GearInfo &info); void importedModel(); + void doneLoadingModel(); + public Q_SLOTS: void clear(); void setGear(const GearInfo &info); diff --git a/armoury/src/mainwindow.cpp b/armoury/src/mainwindow.cpp index 66a20b0..3ee2b9f 100644 --- a/armoury/src/mainwindow.cpp +++ b/armoury/src/mainwindow.cpp @@ -49,13 +49,13 @@ MainWindow::MainWindow(GameData *in_data) }); connect(gearView, &SingleGearView::importedModel, m_api, &PenumbraApi::redrawAll); - materialView = new MaterialView(&data); + materialsView = new QTabWidget(); metadataView = new MetadataView(&data); auto tabWidget = new QTabWidget(); tabWidget->addTab(gearView, i18nc("@title:tab", "Models")); - tabWidget->addTab(materialView, i18nc("@title:tab", "Materials")); + tabWidget->addTab(materialsView, i18nc("@title:tab", "Materials")); tabWidget->addTab(metadataView, i18nc("@title:tab", "Metadata")); tabWidget->setDocumentMode(true); // Don't draw the borders tabWidget->tabBar()->setExpanding(true); @@ -65,6 +65,19 @@ MainWindow::MainWindow(GameData *in_data) connect(fullModelViewer, &FullModelViewer::loadingChanged, this, [this](const bool loading) { gearView->setFMVAvailable(!loading); }); + + connect(gearView, &SingleGearView::doneLoadingModel, this, [this, in_data] { + materialsView->clear(); + + int i = 0; + for (auto material : gearView->getLoadedMaterials()) { + auto materialView = new MtrlPart(in_data); + materialView->load(material); + materialsView->addTab(materialView, i18n("Material %1", i)); // TODO: it would be nice to get the actual material name here + + i++; + } + }); } void MainWindow::setupAdditionalMenus(QMenuBar *menuBar) diff --git a/armoury/src/materialview.cpp b/armoury/src/materialview.cpp deleted file mode 100644 index c92db22..0000000 --- a/armoury/src/materialview.cpp +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-FileCopyrightText: 2024 Joshua Goins -// SPDX-License-Identifier: GPL-3.0-or-later - -#include "materialview.h" - -#include - -MaterialView::MaterialView(GameData *data, QWidget *parent) - : QWidget(parent) - , data(data) -{ - auto layout = new QVBoxLayout(); - setLayout(layout); -} - -#include "moc_materialview.cpp" \ No newline at end of file diff --git a/armoury/src/singlegearview.cpp b/armoury/src/singlegearview.cpp index b09053b..220c89d 100644 --- a/armoury/src/singlegearview.cpp +++ b/armoury/src/singlegearview.cpp @@ -38,6 +38,7 @@ SingleGearView::SingleGearView(GameData *data, FileCache &cache, QWidget *parent connect(this, &SingleGearView::gotMDLPath, this, [this, mdlPathEdit] { mdlPathEdit->setText(gearView->getLoadedGearPath()); + Q_EMIT doneLoadingModel(); }); auto topControlLayout = new QHBoxLayout(); @@ -442,4 +443,18 @@ void SingleGearView::importModel(const QString &filename) Q_EMIT importedModel(); } +QList SingleGearView::getLoadedMaterials() const +{ + QList materialPaths; + + for (int i = 0; i < gearView->part().numModels(); i++) { + auto model = gearView->part().getModel(i); + for (auto material : model.materials) { + materialPaths.push_back(material.mat); + } + } + + return materialPaths; +} + #include "moc_singlegearview.cpp" \ No newline at end of file diff --git a/common/include/filetypes.h b/common/include/filetypes.h index 121d857..380322f 100644 --- a/common/include/filetypes.h +++ b/common/include/filetypes.h @@ -7,7 +7,7 @@ #include "novuscommon_export.h" -enum class FileType { Unknown, ExcelList, ExcelHeader, ExcelData, Model, Texture, ShaderPackage, CharaMakeParams, Skeleton, Dictionary }; +enum class FileType { Unknown, ExcelList, ExcelHeader, ExcelData, Model, Texture, ShaderPackage, CharaMakeParams, Skeleton, Dictionary, Material }; class NOVUSCOMMON_EXPORT FileTypes { diff --git a/common/src/filetypes.cpp b/common/src/filetypes.cpp index 46609cb..e06b0af 100644 --- a/common/src/filetypes.cpp +++ b/common/src/filetypes.cpp @@ -14,7 +14,8 @@ const static QMap extensionToType{{QStringLiteral("exl"), Fil {QStringLiteral("shpk"), FileType::ShaderPackage}, {QStringLiteral("cmp"), FileType::CharaMakeParams}, {QStringLiteral("sklb"), FileType::Skeleton}, - {QStringLiteral("dic"), FileType::Dictionary}}; + {QStringLiteral("dic"), FileType::Dictionary}, + {QStringLiteral("mtrl"), FileType::Material}}; const static QMap typeToName{{FileType::Unknown, i18n("Unknown")}, {FileType::ExcelList, i18n("Excel List")}, @@ -25,7 +26,8 @@ const static QMap typeToName{{FileType::Unknown, i18n("Unknow {FileType::ShaderPackage, i18n("Shader Package")}, {FileType::CharaMakeParams, i18n("Chara Make Params")}, {FileType::Skeleton, i18n("Skeleton")}, - {FileType::Dictionary, i18n("Dictionary")}}; + {FileType::Dictionary, i18n("Dictionary")}, + {FileType::Material, i18n("Material")}}; const static QMap typeToIcon{{FileType::Unknown, QStringLiteral("unknown")}, {FileType::ExcelList, QStringLiteral("x-office-spreadsheet")}, @@ -36,7 +38,8 @@ const static QMap typeToIcon{{FileType::Unknown, QStringLiter {FileType::ShaderPackage, QStringLiteral("paint-pattern-symbolic")}, {FileType::CharaMakeParams, QStringLiteral("step_object_SoftBody-symbolic")}, {FileType::Skeleton, QStringLiteral("user-symbolic")}, - {FileType::Dictionary, QStringLiteral("accessories-dictionary-symbolic")}}; + {FileType::Dictionary, QStringLiteral("accessories-dictionary-symbolic")}, + {FileType::Material, QStringLiteral("map-globe-symbolic")}}; FileType FileTypes::getFileType(const QString &extension) { diff --git a/mateditor/CMakeLists.txt b/mateditor/CMakeLists.txt index b5a46ef..173d225 100644 --- a/mateditor/CMakeLists.txt +++ b/mateditor/CMakeLists.txt @@ -5,12 +5,10 @@ add_executable(novus-mateditor) target_sources(novus-mateditor PRIVATE include/mainwindow.h - include/materialpropertyedit.h include/materialview.h src/main.cpp src/mainwindow.cpp - src/materialpropertyedit.cpp src/materialview.cpp) target_include_directories(novus-mateditor PUBLIC @@ -19,9 +17,10 @@ target_link_libraries(novus-mateditor PRIVATE Novus::Common Novus::MdlPart - Novus::TexPart + Novus::MtrlPart Physis::Physis Physis::Logger + KF6::I18n Qt6::Core Qt6::Widgets) diff --git a/mateditor/src/mainwindow.cpp b/mateditor/src/mainwindow.cpp index c938c0c..0c7435d 100644 --- a/mateditor/src/mainwindow.cpp +++ b/mateditor/src/mainwindow.cpp @@ -3,14 +3,15 @@ #include "mainwindow.h" +#include #include #include #include #include #include -#include "materialpropertyedit.h" #include "materialview.h" +#include "mtrlpart.h" MainWindow::MainWindow(GameData *data) : NovusMainWindow() @@ -27,9 +28,9 @@ MainWindow::MainWindow(GameData *data) dummyWidget->setChildrenCollapsible(false); setCentralWidget(dummyWidget); - auto materialProperty = new MaterialPropertyEdit(data); + auto materialProperty = new MtrlPart(data); materialProperty->setMaximumWidth(400); - materialProperty->setMaterial(m_material); + materialProperty->load(m_material); dummyWidget->addWidget(materialProperty); auto matView = new MaterialView(data, cache); diff --git a/parts/CMakeLists.txt b/parts/CMakeLists.txt index 75ac1cb..d7c7d07 100644 --- a/parts/CMakeLists.txt +++ b/parts/CMakeLists.txt @@ -7,6 +7,7 @@ add_subdirectory(exd) add_subdirectory(exl) add_subdirectory(hex) add_subdirectory(mdl) +add_subdirectory(mtrl) add_subdirectory(shpk) add_subdirectory(sklb) add_subdirectory(tex) \ No newline at end of file diff --git a/parts/mdl/mdlpart.cpp b/parts/mdl/mdlpart.cpp index d1664a7..9118993 100644 --- a/parts/mdl/mdlpart.cpp +++ b/parts/mdl/mdlpart.cpp @@ -39,6 +39,7 @@ MDLPart::MDLPart(GameData *data, FileCache &cache, QWidget *parent) vkWindow->setVulkanInstance(inst); auto widget = QWidget::createWindowContainer(vkWindow); + widget->installEventFilter(vkWindow); viewportLayout->addWidget(widget); @@ -332,4 +333,9 @@ bool MDLPart::wireframe() const return false; } +int MDLPart::numModels() const +{ + return models.size(); +} + #include "moc_mdlpart.cpp" \ No newline at end of file diff --git a/parts/mdl/mdlpart.h b/parts/mdl/mdlpart.h index 987b9e1..ed2f3d2 100644 --- a/parts/mdl/mdlpart.h +++ b/parts/mdl/mdlpart.h @@ -47,6 +47,8 @@ public: void setWireframe(bool wireframe); bool wireframe() const; + int numModels() const; + Q_SIGNALS: void modelChanged(); void skeletonChanged(); diff --git a/parts/mdl/vulkanwindow.cpp b/parts/mdl/vulkanwindow.cpp index ed4f2d7..871f2f8 100644 --- a/parts/mdl/vulkanwindow.cpp +++ b/parts/mdl/vulkanwindow.cpp @@ -37,6 +37,21 @@ void VulkanWindow::exposeEvent(QExposeEvent *) } } +bool VulkanWindow::eventFilter(QObject *watched, QEvent *event) +{ + switch (event->type()) { + case QEvent::Hide: + // QWindow is reset when hiding a widget container without "SurfaceAboutToBeDestroyed" notification (Qt bug, tested on 6.5.1) + m_renderer->destroySwapchain(); + m_initialized = false; + break; + default: + break; + } + // dispatchEvent(event, watched); + return QWindow::eventFilter(watched, event); +} + bool VulkanWindow::event(QEvent *e) { switch (e->type()) { @@ -52,11 +67,16 @@ bool VulkanWindow::event(QEvent *e) resizeEvent->size().height() * screen()->devicePixelRatio()); } } break; - case QEvent::PlatformSurface: - if (dynamic_cast(e)->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed && m_initialized) { + case QEvent::Hide: { + m_renderer->destroySwapchain(); + } break; + case QEvent::PlatformSurface: { + auto surfaceEvent = dynamic_cast(e); + auto surfaceEventType = surfaceEvent->surfaceEventType(); + if (surfaceEventType == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed && m_initialized) { m_renderer->destroySwapchain(); } - break; + } break; case QEvent::MouseButtonPress: { auto mouseEvent = dynamic_cast(e); diff --git a/parts/mdl/vulkanwindow.h b/parts/mdl/vulkanwindow.h index cde24fb..85c9ac0 100644 --- a/parts/mdl/vulkanwindow.h +++ b/parts/mdl/vulkanwindow.h @@ -15,6 +15,7 @@ public: void exposeEvent(QExposeEvent *) override; + bool eventFilter(QObject *watched, QEvent *event) override; bool event(QEvent *e) override; void render(); diff --git a/parts/mtrl/CMakeLists.txt b/parts/mtrl/CMakeLists.txt new file mode 100644 index 0000000..7c89c52 --- /dev/null +++ b/parts/mtrl/CMakeLists.txt @@ -0,0 +1,20 @@ +# SPDX-FileCopyrightText: 2024 Joshua Goins +# SPDX-License-Identifier: CC0-1.0 + +add_library(mtrlpart STATIC) +target_sources(mtrlpart + PRIVATE + knownvalues.h + mtrlpart.cpp + mtrlpart.h) +target_link_libraries(mtrlpart + PUBLIC + Physis::Physis + Novus::TexPart + KF6::I18n + Qt6::Core + Qt6::Widgets) +target_include_directories(mtrlpart PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +target_compile_definitions(mtrlpart PRIVATE TRANSLATION_DOMAIN="novus") + +add_library(Novus::MtrlPart ALIAS mtrlpart) \ No newline at end of file diff --git a/mateditor/src/materialpropertyedit.cpp b/parts/mtrl/knownvalues.h similarity index 66% rename from mateditor/src/materialpropertyedit.cpp rename to parts/mtrl/knownvalues.h index 5990b94..47cb516 100644 --- a/mateditor/src/materialpropertyedit.cpp +++ b/parts/mtrl/knownvalues.h @@ -1,15 +1,6 @@ // SPDX-FileCopyrightText: 2024 Joshua Goins // SPDX-License-Identifier: GPL-3.0-or-later -#include "materialpropertyedit.h" -#include "texpart.h" - -#include -#include -#include -#include -#include - const QHash keys = { // Taken from https://github.com/0ceal0t/Dalamud-VFXEditor {0x2C6C023C, "DecodeDepthBuffer"}, @@ -328,213 +319,3 @@ const QHash keys = { {0xC8BD1DEF, "Specular Map Mode"}, {0x198D11CD, "Color"}, {0xA02F4828, "Multi"}}; - -MaterialPropertyEdit::MaterialPropertyEdit(GameData *data, QWidget *parent) - : QWidget(parent) - , m_data(data) -{ - m_itemsLayout = new QVBoxLayout(this); - - auto shaderPackageLayout = new QHBoxLayout(); - m_itemsLayout->addLayout(shaderPackageLayout); - - m_shaderPackageName = new QLineEdit(); - m_shaderPackageName->setReadOnly(true); - shaderPackageLayout->addWidget(m_shaderPackageName); - - auto selectShaderPackageButton = new QPushButton(i18n("Shaders…")); - shaderPackageLayout->addWidget(selectShaderPackageButton); - - m_tabWidget = new QTabWidget(); - m_itemsLayout->addWidget(m_tabWidget); - - auto propertiesTab = new QWidget(); - m_propertiesLayout = new QVBoxLayout(); - propertiesTab->setLayout(m_propertiesLayout); - - auto texturesTab = new QWidget(); - m_texturesLayout = new QVBoxLayout(); - texturesTab->setLayout(m_texturesLayout); - - auto constantsTab = new QWidget(); - m_constantsLayout = new QVBoxLayout(); - constantsTab->setLayout(m_constantsLayout); - - m_tabWidget->addTab(propertiesTab, i18n("Parameters")); - m_tabWidget->addTab(texturesTab, i18n("Textures")); - m_tabWidget->addTab(constantsTab, i18n("Constants")); - - setLayout(m_itemsLayout); - - rebuild(); -} - -void MaterialPropertyEdit::setMaterial(physis_Material material) -{ - m_material = material; - m_shaderPackageName->setText(QString::fromLatin1(material.shpk_name)); - if (material.shpk_name != nullptr) { - std::string shpkPath = "shader/sm5/shpk/" + std::string(material.shpk_name); - - auto shpkData = physis_gamedata_extract_file(m_data, shpkPath.c_str()); - if (shpkData.data != nullptr) { - m_shpk = physis_parse_shpk(shpkData); - } - } - rebuild(); -} - -void MaterialPropertyEdit::rebuild() -{ - QLayoutItem *child = nullptr; - while ((child = m_propertiesLayout->takeAt(0)) != nullptr) { - child->widget()->setParent(nullptr); - child->widget()->deleteLater(); - } - - for (int i = 0; i < m_shpk.num_material_keys; i++) { - const auto materialKey = m_shpk.material_keys[i]; - - auto groupBox = new QGroupBox(); - m_propertiesLayout->addWidget(groupBox); - - if (keys.contains(materialKey.id)) { - groupBox->setTitle(QString::fromLatin1(keys[materialKey.id])); - } else { - groupBox->setTitle(i18n("Unknown Property %1", QStringLiteral("%1").arg(materialKey.id, 1, 16))); - } - - uint32_t value = 0; - - bool found = false; - for (int j = 0; j < m_material.num_shader_keys; j++) { - auto shaderKey = m_material.shader_keys[j]; - - if (shaderKey.category == materialKey.id) { - value = shaderKey.value; - found = true; - } - } - - // Fall back to default value - if (!found) { - value = materialKey.default_value; - } - - auto layout = new QFormLayout(); - groupBox->setLayout(layout); - - auto label = new QLabel(); - if (keys.contains(value)) { - label->setText(QString::fromLatin1(keys[value])); - } else { - label->setText(i18n("Unknown value %1", QStringLiteral("%1").arg(value, 1, 16))); - } - - layout->addRow(i18n("Value:"), label); - } - - child = nullptr; - while ((child = m_texturesLayout->takeAt(0)) != nullptr) { - child->widget()->setParent(nullptr); - child->widget()->deleteLater(); - } - - for (int i = 0; i < m_material.num_samplers; i++) { - const auto sampler = m_material.samplers[i]; - - QString name; - switch (sampler.texture_usage) { - case TextureUsage::Sampler: - case TextureUsage::Sampler0: - case TextureUsage::Sampler1: - name = i18n("Generic"); - break; - case TextureUsage::SamplerCatchlight: - name = i18n("Catchlight"); - break; - case TextureUsage::SamplerColorMap0: - name = i18n("Color Map 0"); - break; - case TextureUsage::SamplerColorMap1: - name = i18n("Color Map 1"); - break; - case TextureUsage::SamplerDiffuse: - name = i18n("Diffuse"); - break; - case TextureUsage::SamplerEnvMap: - name = i18n("Environment Map"); - break; - case TextureUsage::SamplerMask: - name = i18n("Mask"); - break; - case TextureUsage::SamplerNormal: - name = i18n("Normal"); - break; - case TextureUsage::SamplerNormalMap0: - name = i18n("Normal Map 0"); - break; - case TextureUsage::SamplerNormalMap1: - name = i18n("Normal Map 1"); - break; - case TextureUsage::SamplerReflection: - name = i18n("Reflection"); - break; - case TextureUsage::SamplerSpecular: - name = i18n("Specular"); - break; - case TextureUsage::SamplerSpecularMap0: - name = i18n("Specular Map 0"); - break; - case TextureUsage::SamplerSpecularMap1: - name = i18n("Specular Map 1"); - break; - case TextureUsage::SamplerWaveMap: - name = i18n("Wave Map"); - break; - case TextureUsage::SamplerWaveletMap0: - name = i18n("Wavelet Map 0"); - break; - case TextureUsage::SamplerWaveletMap1: - name = i18n("Wavelet Map 1"); - break; - case TextureUsage::SamplerWhitecapMap: - name = i18n("Whitecap Map"); - break; - default: - name = i18n("Unknown"); - break; - } - - auto groupBox = new QGroupBox(name); - m_texturesLayout->addWidget(groupBox); - - auto layout = new QFormLayout(); - groupBox->setLayout(layout); - - auto texWidget = new TexPart(m_data); - texWidget->load(physis_gamedata_extract_file(m_data, m_material.textures[i])); - layout->addRow(i18n("Value:"), texWidget); - } - - child = nullptr; - while ((child = m_constantsLayout->takeAt(0)) != nullptr) { - child->widget()->setParent(nullptr); - child->widget()->deleteLater(); - } - - for (int i = 0; i < m_material.num_constants; i++) { - const auto constant = m_material.constants[i]; - - auto groupBox = new QGroupBox(QString::number(constant.id)); - m_constantsLayout->addWidget(groupBox); - - auto layout = new QFormLayout(); - groupBox->setLayout(layout); - - auto label = new QLabel(QString::number(constant.value)); - layout->addRow(i18n("Value:"), label); - } -} - -#include "moc_materialpropertyedit.cpp" diff --git a/parts/mtrl/mtrlpart.cpp b/parts/mtrl/mtrlpart.cpp new file mode 100644 index 0000000..8cf4750 --- /dev/null +++ b/parts/mtrl/mtrlpart.cpp @@ -0,0 +1,224 @@ +// SPDX-FileCopyrightText: 2024 Joshua Goins +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "mtrlpart.h" + +#include +#include +#include +#include +#include +#include + +#include "knownvalues.h" +#include "texpart.h" + +MtrlPart::MtrlPart(GameData *data, QWidget *parent) + : QWidget(parent) + , m_data(data) +{ + m_itemsLayout = new QVBoxLayout(this); + + auto shaderPackageLayout = new QHBoxLayout(); + m_itemsLayout->addLayout(shaderPackageLayout); + + m_shaderPackageName = new QLineEdit(); + m_shaderPackageName->setReadOnly(true); + shaderPackageLayout->addWidget(m_shaderPackageName); + + auto selectShaderPackageButton = new QPushButton(i18n("Shaders…")); + shaderPackageLayout->addWidget(selectShaderPackageButton); + + m_tabWidget = new QTabWidget(); + m_itemsLayout->addWidget(m_tabWidget); + + auto propertiesTab = new QWidget(); + m_propertiesLayout = new QVBoxLayout(); + propertiesTab->setLayout(m_propertiesLayout); + + auto texturesTab = new QWidget(); + m_texturesLayout = new QVBoxLayout(); + texturesTab->setLayout(m_texturesLayout); + + auto constantsTab = new QWidget(); + m_constantsLayout = new QVBoxLayout(); + constantsTab->setLayout(m_constantsLayout); + + m_tabWidget->addTab(propertiesTab, i18n("Parameters")); + m_tabWidget->addTab(texturesTab, i18n("Textures")); + m_tabWidget->addTab(constantsTab, i18n("Constants")); + + setLayout(m_itemsLayout); + + rebuild(); +} + +void MtrlPart::load(physis_Material file) +{ + m_material = file; + m_shaderPackageName->setText(QString::fromLatin1(m_material.shpk_name)); + if (m_material.shpk_name != nullptr) { + std::string shpkPath = "shader/sm5/shpk/" + std::string(m_material.shpk_name); + + auto shpkData = physis_gamedata_extract_file(m_data, shpkPath.c_str()); + if (shpkData.data != nullptr) { + m_shpk = physis_parse_shpk(shpkData); + } + } + rebuild(); +} + +void MtrlPart::rebuild() +{ + QLayoutItem *child = nullptr; + while ((child = m_propertiesLayout->takeAt(0)) != nullptr) { + child->widget()->setParent(nullptr); + child->widget()->deleteLater(); + } + + for (int i = 0; i < m_shpk.num_material_keys; i++) { + const auto materialKey = m_shpk.material_keys[i]; + + auto groupBox = new QGroupBox(); + m_propertiesLayout->addWidget(groupBox); + + if (keys.contains(materialKey.id)) { + groupBox->setTitle(QString::fromLatin1(keys[materialKey.id])); + } else { + groupBox->setTitle(i18n("Unknown Property %1", QStringLiteral("%1").arg(materialKey.id, 1, 16))); + } + + uint32_t value = 0; + + bool found = false; + for (int j = 0; j < m_material.num_shader_keys; j++) { + auto shaderKey = m_material.shader_keys[j]; + + if (shaderKey.category == materialKey.id) { + value = shaderKey.value; + found = true; + } + } + + // Fall back to default value + if (!found) { + value = materialKey.default_value; + } + + auto layout = new QFormLayout(); + groupBox->setLayout(layout); + + auto label = new QLabel(); + if (keys.contains(value)) { + label->setText(QString::fromLatin1(keys[value])); + } else { + label->setText(i18n("Unknown value %1", QStringLiteral("%1").arg(value, 1, 16))); + } + + layout->addRow(i18n("Value:"), label); + } + + child = nullptr; + while ((child = m_texturesLayout->takeAt(0)) != nullptr) { + child->widget()->setParent(nullptr); + child->widget()->deleteLater(); + } + + for (int i = 0; i < m_material.num_samplers; i++) { + const auto sampler = m_material.samplers[i]; + + QString name; + switch (sampler.texture_usage) { + case TextureUsage::Sampler: + case TextureUsage::Sampler0: + case TextureUsage::Sampler1: + name = i18n("Generic"); + break; + case TextureUsage::SamplerCatchlight: + name = i18n("Catchlight"); + break; + case TextureUsage::SamplerColorMap0: + name = i18n("Color Map 0"); + break; + case TextureUsage::SamplerColorMap1: + name = i18n("Color Map 1"); + break; + case TextureUsage::SamplerDiffuse: + name = i18n("Diffuse"); + break; + case TextureUsage::SamplerEnvMap: + name = i18n("Environment Map"); + break; + case TextureUsage::SamplerMask: + name = i18n("Mask"); + break; + case TextureUsage::SamplerNormal: + name = i18n("Normal"); + break; + case TextureUsage::SamplerNormalMap0: + name = i18n("Normal Map 0"); + break; + case TextureUsage::SamplerNormalMap1: + name = i18n("Normal Map 1"); + break; + case TextureUsage::SamplerReflection: + name = i18n("Reflection"); + break; + case TextureUsage::SamplerSpecular: + name = i18n("Specular"); + break; + case TextureUsage::SamplerSpecularMap0: + name = i18n("Specular Map 0"); + break; + case TextureUsage::SamplerSpecularMap1: + name = i18n("Specular Map 1"); + break; + case TextureUsage::SamplerWaveMap: + name = i18n("Wave Map"); + break; + case TextureUsage::SamplerWaveletMap0: + name = i18n("Wavelet Map 0"); + break; + case TextureUsage::SamplerWaveletMap1: + name = i18n("Wavelet Map 1"); + break; + case TextureUsage::SamplerWhitecapMap: + name = i18n("Whitecap Map"); + break; + default: + name = i18n("Unknown"); + break; + } + + auto groupBox = new QGroupBox(name); + m_texturesLayout->addWidget(groupBox); + + auto layout = new QFormLayout(); + groupBox->setLayout(layout); + + auto texWidget = new TexPart(m_data); + texWidget->load(physis_gamedata_extract_file(m_data, m_material.textures[i])); + layout->addRow(i18n("Value:"), texWidget); + } + + child = nullptr; + while ((child = m_constantsLayout->takeAt(0)) != nullptr) { + child->widget()->setParent(nullptr); + child->widget()->deleteLater(); + } + + for (int i = 0; i < m_material.num_constants; i++) { + const auto constant = m_material.constants[i]; + + auto groupBox = new QGroupBox(QString::number(constant.id)); + m_constantsLayout->addWidget(groupBox); + + auto layout = new QFormLayout(); + groupBox->setLayout(layout); + + auto label = new QLabel(QString::number(constant.value)); + layout->addRow(i18n("Value:"), label); + } +} + +#include "moc_mtrlpart.cpp" \ No newline at end of file diff --git a/mateditor/include/materialpropertyedit.h b/parts/mtrl/mtrlpart.h similarity index 79% rename from mateditor/include/materialpropertyedit.h rename to parts/mtrl/mtrlpart.h index f89fd7a..0540c06 100644 --- a/mateditor/include/materialpropertyedit.h +++ b/parts/mtrl/mtrlpart.h @@ -11,14 +11,14 @@ #include #include -class MaterialPropertyEdit : public QWidget +class MtrlPart : public QWidget { Q_OBJECT public: - explicit MaterialPropertyEdit(GameData *data, QWidget *parent = nullptr); + explicit MtrlPart(GameData *data, QWidget *parent = nullptr); - void setMaterial(physis_Material material); + void load(physis_Material file); private: void rebuild(); @@ -35,4 +35,4 @@ private: physis_SHPK m_shpk = {}; GameData *m_data = nullptr; -}; +}; \ No newline at end of file diff --git a/renderer/src/rendermanager.cpp b/renderer/src/rendermanager.cpp index 5ce8eb5..fdac8bf 100644 --- a/renderer/src/rendermanager.cpp +++ b/renderer/src/rendermanager.cpp @@ -350,11 +350,10 @@ void RenderManager::resize(VkSurfaceKHR surface, int width, int height) void RenderManager::destroySwapchain() { - // TODO: port to new swapchain aPI - /*if (swapchain != VK_NULL_HANDLE) { - vkDestroySwapchainKHR(device, swapchain, nullptr); - swapchain = VK_NULL_HANDLE; - }*/ + if (m_device->swapChain->swapchain != VK_NULL_HANDLE) { + vkDestroySwapchainKHR(m_device->device, m_device->swapChain->swapchain, nullptr); + m_device->swapChain->swapchain = VK_NULL_HANDLE; + } } void RenderManager::render(const std::vector &models) diff --git a/sagasu/CMakeLists.txt b/sagasu/CMakeLists.txt index a6d26e6..95f6043 100644 --- a/sagasu/CMakeLists.txt +++ b/sagasu/CMakeLists.txt @@ -44,6 +44,7 @@ target_link_libraries(novus-sagasu Novus::ExdPart Novus::TexPart Novus::DicPart + Novus::MtrlPart Physis::Logger Qt6::Concurrent Qt6::Network) diff --git a/sagasu/src/indexer.cpp b/sagasu/src/indexer.cpp index 5dd90df..94b24a6 100644 --- a/sagasu/src/indexer.cpp +++ b/sagasu/src/indexer.cpp @@ -256,6 +256,7 @@ int main(int argc, char *argv[]) database.addFile(QStringLiteral("chara/xls/bonedeformer/human.pbd")); database.addFile(QStringLiteral("chara/xls/charamake/human.cmp")); database.addFile(QStringLiteral("chara/human/c0101/skeleton/base/b0001/skl_c0101b0001.sklb")); + database.addFile(QStringLiteral("chara/equipment/e0028/material/v0001/mt_c0101e0028_top_a.mtrl")); return 0; } \ No newline at end of file diff --git a/sagasu/src/mainwindow.cpp b/sagasu/src/mainwindow.cpp index 34b9c79..69a2b79 100644 --- a/sagasu/src/mainwindow.cpp +++ b/sagasu/src/mainwindow.cpp @@ -24,6 +24,7 @@ #include "filetypes.h" #include "hexpart.h" #include "mdlpart.h" +#include "mtrlpart.h" #include "shpkpart.h" #include "sklbpart.h" #include "texpart.h" @@ -132,6 +133,11 @@ void MainWindow::refreshParts(const QString &path) dicWidget->load(file); partHolder->addTab(dicWidget, i18nc("@title:tab", "Dictionary")); } break; + case FileType::Material: { + auto mtrlWidget = new MtrlPart(data); + mtrlWidget->load(physis_material_parse(file)); + partHolder->addTab(mtrlWidget, i18nc("@title:tab", "Material")); + } break; default: break; }