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; }