1
Fork 0
mirror of https://github.com/redstrate/Novus.git synced 2025-04-20 03:37:46 +00:00

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.
This commit is contained in:
Joshua Goins 2024-04-27 13:45:08 -04:00
parent 5c31965691
commit 51ea3c3920
24 changed files with 342 additions and 285 deletions

View file

@ -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

View file

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

View file

@ -1,22 +0,0 @@
// SPDX-FileCopyrightText: 2024 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "filecache.h"
#include "gearview.h"
#include <QWidget>
struct GameData;
class MaterialView : public QWidget
{
Q_OBJECT
public:
explicit MaterialView(GameData *data, QWidget *parent = nullptr);
private:
GameData *data = nullptr;
};

View file

@ -18,6 +18,7 @@ public:
explicit SingleGearView(GameData *data, FileCache &cache, QWidget *parent = nullptr);
QString getLoadedGearPath() const;
QList<physis_Material> 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);

View file

@ -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)

View file

@ -1,16 +0,0 @@
// SPDX-FileCopyrightText: 2024 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: GPL-3.0-or-later
#include "materialview.h"
#include <QVBoxLayout>
MaterialView::MaterialView(GameData *data, QWidget *parent)
: QWidget(parent)
, data(data)
{
auto layout = new QVBoxLayout();
setLayout(layout);
}
#include "moc_materialview.cpp"

View file

@ -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<physis_Material> SingleGearView::getLoadedMaterials() const
{
QList<physis_Material> 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"

View file

@ -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
{

View file

@ -14,7 +14,8 @@ const static QMap<QString, FileType> 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<FileType, QString> typeToName{{FileType::Unknown, i18n("Unknown")},
{FileType::ExcelList, i18n("Excel List")},
@ -25,7 +26,8 @@ const static QMap<FileType, QString> 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<FileType, QString> typeToIcon{{FileType::Unknown, QStringLiteral("unknown")},
{FileType::ExcelList, QStringLiteral("x-office-spreadsheet")},
@ -36,7 +38,8 @@ const static QMap<FileType, QString> 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)
{

View file

@ -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)

View file

@ -3,14 +3,15 @@
#include "mainwindow.h"
#include <KLocalizedString>
#include <QApplication>
#include <QListWidget>
#include <QMenuBar>
#include <QSplitter>
#include <physis.hpp>
#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);

View file

@ -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)

View file

@ -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"

View file

@ -47,6 +47,8 @@ public:
void setWireframe(bool wireframe);
bool wireframe() const;
int numModels() const;
Q_SIGNALS:
void modelChanged();
void skeletonChanged();

View file

@ -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<QPlatformSurfaceEvent *>(e)->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed && m_initialized) {
case QEvent::Hide: {
m_renderer->destroySwapchain();
} break;
case QEvent::PlatformSurface: {
auto surfaceEvent = dynamic_cast<QPlatformSurfaceEvent *>(e);
auto surfaceEventType = surfaceEvent->surfaceEventType();
if (surfaceEventType == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed && m_initialized) {
m_renderer->destroySwapchain();
}
break;
} break;
case QEvent::MouseButtonPress: {
auto mouseEvent = dynamic_cast<QMouseEvent *>(e);

View file

@ -15,6 +15,7 @@ public:
void exposeEvent(QExposeEvent *) override;
bool eventFilter(QObject *watched, QEvent *event) override;
bool event(QEvent *e) override;
void render();

20
parts/mtrl/CMakeLists.txt Normal file
View file

@ -0,0 +1,20 @@
# SPDX-FileCopyrightText: 2024 Joshua Goins <josh@redstrate.com>
# 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)

View file

@ -1,15 +1,6 @@
// SPDX-FileCopyrightText: 2024 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: GPL-3.0-or-later
#include "materialpropertyedit.h"
#include "texpart.h"
#include <KLocalizedString>
#include <QFormLayout>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QPushButton>
const QHash<uint, const char *> keys = {
// Taken from https://github.com/0ceal0t/Dalamud-VFXEditor
{0x2C6C023C, "DecodeDepthBuffer"},
@ -328,213 +319,3 @@ const QHash<uint, const char *> 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"

224
parts/mtrl/mtrlpart.cpp Normal file
View file

@ -0,0 +1,224 @@
// SPDX-FileCopyrightText: 2024 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: GPL-3.0-or-later
#include "mtrlpart.h"
#include <KLocalizedString>
#include <QFormLayout>
#include <QGroupBox>
#include <QPushButton>
#include <QVBoxLayout>
#include <physis.hpp>
#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"

View file

@ -11,14 +11,14 @@
#include <QTabWidget>
#include <physis.hpp>
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;
};
};

View file

@ -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<DrawObject> &models)

View file

@ -44,6 +44,7 @@ target_link_libraries(novus-sagasu
Novus::ExdPart
Novus::TexPart
Novus::DicPart
Novus::MtrlPart
Physis::Logger
Qt6::Concurrent
Qt6::Network)

View file

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

View file

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