1
Fork 0
mirror of https://github.com/redstrate/Novus.git synced 2025-04-23 20:47:45 +00:00

Make armoury's CMP editor a general-purpose part, add it to sagasu

This commit is contained in:
Joshua Goins 2023-10-13 15:20:01 -04:00
parent 70cbd9672c
commit 6caedba0d9
10 changed files with 226 additions and 175 deletions

View file

@ -35,11 +35,11 @@ target_link_libraries(novus-armoury PUBLIC
Qt6::Core Qt6::Core
Qt6::Widgets Qt6::Widgets
Qt6::Concurrent Qt6::Concurrent
magic_enum
physis physis
z z
physis-logger physis-logger
mdlpart mdlpart
cmppart
imgui imgui
novus-common) novus-common)

View file

@ -3,55 +3,12 @@
#pragma once #pragma once
#include <QDoubleSpinBox> #include "cmppart.h"
#include <QWidget>
#include <physis.hpp>
class RaceTreeData : public QObject class CmpEditor : public CmpPart
{
Q_OBJECT
public:
RaceTreeData(Race race, Subrace subrace)
: race(race)
, subrace(subrace)
{
}
Race race;
Subrace subrace;
};
class CmpEditor : public QWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit CmpEditor(GameData *data, QWidget *parent = nullptr); explicit CmpEditor(GameData *data, QWidget *parent = nullptr);
private:
void loadRaceData(Race race, Subrace subrace);
GameData *data = nullptr;
physis_CMP cmp;
QDoubleSpinBox *maleMinSize = nullptr;
QDoubleSpinBox *maleMaxSize = nullptr;
QDoubleSpinBox *maleMinTail = nullptr;
QDoubleSpinBox *maleMaxTail = nullptr;
QDoubleSpinBox *femaleMinSize = nullptr;
QDoubleSpinBox *femaleMaxSize = nullptr;
QDoubleSpinBox *femaleMinTail = nullptr;
QDoubleSpinBox *femaleMaxTail = nullptr;
QDoubleSpinBox *bustMinX = nullptr;
QDoubleSpinBox *bustMinY = nullptr;
QDoubleSpinBox *bustMinZ = nullptr;
QDoubleSpinBox *bustMaxX = nullptr;
QDoubleSpinBox *bustMaxY = nullptr;
QDoubleSpinBox *bustMaxZ = nullptr;
}; };

View file

@ -3,137 +3,12 @@
#include "cmpeditor.h" #include "cmpeditor.h"
#include <QFormLayout>
#include <QGroupBox>
#include <QHBoxLayout>
#include <QTreeWidget>
#include "magic_enum.hpp"
// TODO: move this to physis
struct RaceTree {
Race baseRace;
std::vector<Subrace> subRaces;
};
std::vector<RaceTree> raceTree = {{Race::Hyur, {Subrace::Midlander, Subrace::Highlander}},
{Race::Elezen, {Subrace::Wildwood, Subrace::Duskwight}},
{Race::Miqote, {Subrace::Seeker, Subrace::Keeper}},
{Race::Roegadyn, {Subrace::SeaWolf, Subrace::Hellion}},
{Race::Lalafell, {Subrace::Plainsfolk, Subrace::Dunesfolk}},
{Race::AuRa, {Subrace::Raen, Subrace::Xaela}},
{Race::Hrothgar, {Subrace::Hellion, Subrace::Lost}},
{Race::Viera, {Subrace::Rava, Subrace::Veena}}};
CmpEditor::CmpEditor(GameData *data, QWidget *parent) CmpEditor::CmpEditor(GameData *data, QWidget *parent)
: QWidget(parent) : CmpPart(data)
, data(data)
{ {
setWindowTitle(QStringLiteral("CMP Editor")); setWindowTitle(QStringLiteral("CMP Editor"));
auto layout = new QHBoxLayout(); load(physis_gamedata_extract_file(data, "chara/xls/charamake/human.cmp"));
setLayout(layout);
cmp = physis_cmp_parse(physis_gamedata_extract_file(data, "chara/xls/charamake/human.cmp"));
auto raceListWidget = new QTreeWidget();
raceListWidget->setMaximumWidth(200);
layout->addWidget(raceListWidget);
for (auto race : raceTree) {
auto item = new QTreeWidgetItem();
item->setText(0, QLatin1String(magic_enum::enum_name(race.baseRace).data()));
raceListWidget->addTopLevelItem(item);
for (auto subrace : race.subRaces) {
auto subItem = new QTreeWidgetItem();
subItem->setText(0, QLatin1String(magic_enum::enum_name(subrace).data()));
subItem->setData(0, Qt::UserRole, QVariant::fromValue(new RaceTreeData(race.baseRace, subrace)));
item->addChild(subItem);
}
}
raceListWidget->expandAll();
connect(raceListWidget, &QTreeWidget::itemClicked, [this](QTreeWidgetItem *item, int column) {
if (auto treeData = qvariant_cast<RaceTreeData *>(item->data(0, Qt::UserRole))) {
loadRaceData(treeData->race, treeData->subrace);
}
});
auto detailBox = new QGroupBox();
layout->addWidget(detailBox);
auto detailBoxLayout = new QFormLayout();
detailBox->setLayout(detailBoxLayout);
maleMinSize = new QDoubleSpinBox();
detailBoxLayout->addRow(QStringLiteral("Male Min Size"), maleMinSize);
maleMaxSize = new QDoubleSpinBox();
detailBoxLayout->addRow(QStringLiteral("Male Max Size"), maleMaxSize);
maleMinTail = new QDoubleSpinBox();
detailBoxLayout->addRow(QStringLiteral("Male Min Tail"), maleMinTail);
maleMaxTail = new QDoubleSpinBox();
detailBoxLayout->addRow(QStringLiteral("Male Max Tail"), maleMaxTail);
femaleMinSize = new QDoubleSpinBox();
detailBoxLayout->addRow(QStringLiteral("Female Min Size"), femaleMinSize);
femaleMaxSize = new QDoubleSpinBox();
detailBoxLayout->addRow(QStringLiteral("Female Max Size"), femaleMaxSize);
femaleMinTail = new QDoubleSpinBox();
detailBoxLayout->addRow(QStringLiteral("Female Min Tail"), femaleMinTail);
femaleMaxTail = new QDoubleSpinBox();
detailBoxLayout->addRow(QStringLiteral("Female Max Tail"), femaleMaxTail);
bustMinX = new QDoubleSpinBox();
detailBoxLayout->addRow(QStringLiteral("Bust Min X"), bustMinX);
bustMinY = new QDoubleSpinBox();
detailBoxLayout->addRow(QStringLiteral("Bust Min Y"), bustMinY);
bustMinZ = new QDoubleSpinBox();
detailBoxLayout->addRow(QStringLiteral("Bust Min Z"), bustMinZ);
bustMaxX = new QDoubleSpinBox();
detailBoxLayout->addRow(QStringLiteral("Bust Max X"), bustMaxX);
bustMaxY = new QDoubleSpinBox();
detailBoxLayout->addRow(QStringLiteral("Bust Max Y"), bustMaxY);
bustMaxZ = new QDoubleSpinBox();
detailBoxLayout->addRow(QStringLiteral("Bust Max Z"), bustMaxZ);
loadRaceData(Race::Hyur, Subrace::Midlander);
}
void CmpEditor::loadRaceData(Race race, Subrace subrace)
{
auto raceData = physis_cmp_get_racial_scaling_parameters(cmp, race, subrace);
maleMinSize->setValue(raceData.male_min_size);
maleMaxSize->setValue(raceData.male_max_size);
maleMinTail->setValue(raceData.male_min_tail);
maleMaxTail->setValue(raceData.male_max_tail);
femaleMinSize->setValue(raceData.female_min_size);
femaleMaxSize->setValue(raceData.female_max_size);
femaleMinTail->setValue(raceData.female_min_tail);
femaleMaxTail->setValue(raceData.female_max_tail);
bustMinX->setValue(raceData.bust_min_x);
bustMinY->setValue(raceData.bust_min_y);
bustMinZ->setValue(raceData.bust_min_z);
bustMaxX->setValue(raceData.bust_max_x);
bustMaxY->setValue(raceData.bust_max_y);
bustMaxZ->setValue(raceData.bust_max_z);
} }
#include "moc_cmpeditor.cpp" #include "moc_cmpeditor.cpp"

View file

@ -1,6 +1,7 @@
# SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com> # SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
# SPDX-License-Identifier: CC0-1.0 # SPDX-License-Identifier: CC0-1.0
add_subdirectory(cmp)
add_subdirectory(exd) add_subdirectory(exd)
add_subdirectory(exl) add_subdirectory(exl)
add_subdirectory(hex) add_subdirectory(hex)

9
parts/cmp/CMakeLists.txt Normal file
View file

@ -0,0 +1,9 @@
# SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
# SPDX-License-Identifier: CC0-1.0
add_library(cmppart STATIC)
target_sources(cmppart PRIVATE
cmppart.cpp
cmppart.h)
target_link_libraries(cmppart PUBLIC magic_enum physis z Qt6::Core Qt6::Widgets)
target_include_directories(cmppart PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

140
parts/cmp/cmppart.cpp Normal file
View file

@ -0,0 +1,140 @@
// SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: GPL-3.0-or-later
#include "cmppart.h"
#include <QFormLayout>
#include <QGroupBox>
#include <QTreeWidget>
#include <physis.hpp>
#include "magic_enum.hpp"
// TODO: move this to physis
struct RaceTree {
Race baseRace;
std::vector<Subrace> subRaces;
};
const std::vector<RaceTree> raceTree = {{Race::Hyur, {Subrace::Midlander, Subrace::Highlander}},
{Race::Elezen, {Subrace::Wildwood, Subrace::Duskwight}},
{Race::Miqote, {Subrace::Seeker, Subrace::Keeper}},
{Race::Roegadyn, {Subrace::SeaWolf, Subrace::Hellion}},
{Race::Lalafell, {Subrace::Plainsfolk, Subrace::Dunesfolk}},
{Race::AuRa, {Subrace::Raen, Subrace::Xaela}},
{Race::Hrothgar, {Subrace::Hellion, Subrace::Lost}},
{Race::Viera, {Subrace::Rava, Subrace::Veena}}};
CmpPart::CmpPart(GameData *data)
: data(data)
{
layout = new QHBoxLayout();
setLayout(layout);
}
void CmpPart::load(physis_Buffer file)
{
cmp = physis_cmp_parse(file);
auto raceListWidget = new QTreeWidget();
raceListWidget->setMaximumWidth(200);
raceListWidget->setHeaderLabel(QStringLiteral("Race"));
layout->addWidget(raceListWidget);
for (auto race : raceTree) {
auto item = new QTreeWidgetItem();
item->setText(0, QLatin1String(magic_enum::enum_name(race.baseRace).data()));
raceListWidget->addTopLevelItem(item);
for (auto subrace : race.subRaces) {
auto subItem = new QTreeWidgetItem();
subItem->setText(0, QLatin1String(magic_enum::enum_name(subrace).data()));
subItem->setData(0, Qt::UserRole, QVariant::fromValue(new RaceTreeData(race.baseRace, subrace)));
item->addChild(subItem);
}
}
raceListWidget->expandAll();
connect(raceListWidget, &QTreeWidget::itemClicked, [this](QTreeWidgetItem *item, int column) {
if (auto treeData = qvariant_cast<RaceTreeData *>(item->data(0, Qt::UserRole))) {
loadRaceData(treeData->race, treeData->subrace);
}
});
auto detailBox = new QGroupBox();
layout->addWidget(detailBox);
auto detailBoxLayout = new QFormLayout();
detailBox->setLayout(detailBoxLayout);
maleMinSize = new QDoubleSpinBox();
detailBoxLayout->addRow(QStringLiteral("Male Min Size"), maleMinSize);
maleMaxSize = new QDoubleSpinBox();
detailBoxLayout->addRow(QStringLiteral("Male Max Size"), maleMaxSize);
maleMinTail = new QDoubleSpinBox();
detailBoxLayout->addRow(QStringLiteral("Male Min Tail"), maleMinTail);
maleMaxTail = new QDoubleSpinBox();
detailBoxLayout->addRow(QStringLiteral("Male Max Tail"), maleMaxTail);
femaleMinSize = new QDoubleSpinBox();
detailBoxLayout->addRow(QStringLiteral("Female Min Size"), femaleMinSize);
femaleMaxSize = new QDoubleSpinBox();
detailBoxLayout->addRow(QStringLiteral("Female Max Size"), femaleMaxSize);
femaleMinTail = new QDoubleSpinBox();
detailBoxLayout->addRow(QStringLiteral("Female Min Tail"), femaleMinTail);
femaleMaxTail = new QDoubleSpinBox();
detailBoxLayout->addRow(QStringLiteral("Female Max Tail"), femaleMaxTail);
bustMinX = new QDoubleSpinBox();
detailBoxLayout->addRow(QStringLiteral("Bust Min X"), bustMinX);
bustMinY = new QDoubleSpinBox();
detailBoxLayout->addRow(QStringLiteral("Bust Min Y"), bustMinY);
bustMinZ = new QDoubleSpinBox();
detailBoxLayout->addRow(QStringLiteral("Bust Min Z"), bustMinZ);
bustMaxX = new QDoubleSpinBox();
detailBoxLayout->addRow(QStringLiteral("Bust Max X"), bustMaxX);
bustMaxY = new QDoubleSpinBox();
detailBoxLayout->addRow(QStringLiteral("Bust Max Y"), bustMaxY);
bustMaxZ = new QDoubleSpinBox();
detailBoxLayout->addRow(QStringLiteral("Bust Max Z"), bustMaxZ);
loadRaceData(Race::Hyur, Subrace::Midlander);
}
void CmpPart::loadRaceData(Race race, Subrace subrace)
{
auto raceData = physis_cmp_get_racial_scaling_parameters(cmp, race, subrace);
maleMinSize->setValue(raceData.male_min_size);
maleMaxSize->setValue(raceData.male_max_size);
maleMinTail->setValue(raceData.male_min_tail);
maleMaxTail->setValue(raceData.male_max_tail);
femaleMinSize->setValue(raceData.female_min_size);
femaleMaxSize->setValue(raceData.female_max_size);
femaleMinTail->setValue(raceData.female_min_tail);
femaleMaxTail->setValue(raceData.female_max_tail);
bustMinX->setValue(raceData.bust_min_x);
bustMinY->setValue(raceData.bust_min_y);
bustMinZ->setValue(raceData.bust_min_z);
bustMaxX->setValue(raceData.bust_max_x);
bustMaxY->setValue(raceData.bust_max_y);
bustMaxZ->setValue(raceData.bust_max_z);
}
#include "moc_cmppart.cpp"

62
parts/cmp/cmppart.h Normal file
View file

@ -0,0 +1,62 @@
// SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <QDoubleSpinBox>
#include <QHBoxLayout>
#include <QWidget>
#include <physis.hpp>
class RaceTreeData : public QObject
{
Q_OBJECT
public:
RaceTreeData(Race race, Subrace subrace)
: race(race)
, subrace(subrace)
{
}
Race race;
Subrace subrace;
};
class CmpPart : public QWidget
{
Q_OBJECT
public:
explicit CmpPart(GameData *data);
void load(physis_Buffer file);
private:
void loadRaceData(Race race, Subrace subrace);
GameData *data = nullptr;
physis_CMP cmp;
QDoubleSpinBox *maleMinSize = nullptr;
QDoubleSpinBox *maleMaxSize = nullptr;
QDoubleSpinBox *maleMinTail = nullptr;
QDoubleSpinBox *maleMaxTail = nullptr;
QDoubleSpinBox *femaleMinSize = nullptr;
QDoubleSpinBox *femaleMaxSize = nullptr;
QDoubleSpinBox *femaleMinTail = nullptr;
QDoubleSpinBox *femaleMaxTail = nullptr;
QDoubleSpinBox *bustMinX = nullptr;
QDoubleSpinBox *bustMinY = nullptr;
QDoubleSpinBox *bustMinZ = nullptr;
QDoubleSpinBox *bustMaxX = nullptr;
QDoubleSpinBox *bustMaxY = nullptr;
QDoubleSpinBox *bustMaxZ = nullptr;
QHBoxLayout *layout = nullptr;
};

View file

@ -22,6 +22,6 @@ target_sources(novus-sagasu PRIVATE
src/filepropertieswindow.cpp src/filepropertieswindow.cpp
src/filetreemodel.cpp) src/filetreemodel.cpp)
target_include_directories(novus-sagasu PRIVATE include) target_include_directories(novus-sagasu PRIVATE include)
target_link_libraries(novus-sagasu PRIVATE Qt6::Concurrent shpkpart hexpart exlpart mdlpart exdpart texpart novus-sagasu-static) target_link_libraries(novus-sagasu PRIVATE Qt6::Concurrent cmppart shpkpart hexpart exlpart mdlpart exdpart texpart novus-sagasu-static)
install(TARGETS novus-sagasu ${KF${QT_MAJOR_VERSION}_INSTALL_TARGETS_DEFAULT_ARGS}) install(TARGETS novus-sagasu ${KF${QT_MAJOR_VERSION}_INSTALL_TARGETS_DEFAULT_ARGS})

View file

@ -14,7 +14,8 @@ const std::array known_folders{"common",
"chara/equipment/e0000/texture", "chara/equipment/e0000/texture",
"chara/human/c0101/obj/face/f0001/model", "chara/human/c0101/obj/face/f0001/model",
"shader/sm5/shpk", "shader/sm5/shpk",
"chara/xls/bonedeformer"}; "chara/xls/bonedeformer",
"chara/xls/charamake"};
const std::array common_font{"common/VulgarWordsFilter.dic", const std::array common_font{"common/VulgarWordsFilter.dic",
"common/VulgarWordsFilter_party.dic", "common/VulgarWordsFilter_party.dic",
@ -165,6 +166,7 @@ int main(int argc, char *argv[])
database.addFile(QStringLiteral("chara/human/c0101/obj/face/f0001/model/c0101f0001_fac.mdl")); database.addFile(QStringLiteral("chara/human/c0101/obj/face/f0001/model/c0101f0001_fac.mdl"));
database.addFile(QStringLiteral("shader/sm5/shpk/character.shpk")); database.addFile(QStringLiteral("shader/sm5/shpk/character.shpk"));
database.addFile(QStringLiteral("chara/xls/bonedeformer/human.pbd")); database.addFile(QStringLiteral("chara/xls/bonedeformer/human.pbd"));
database.addFile(QStringLiteral("chara/xls/charamake/human.cmp"));
/*const QString gameDir{getGameDirectory()}; /*const QString gameDir{getGameDirectory()};
const std::string gameDirStd{gameDir.toStdString()}; const std::string gameDirStd{gameDir.toStdString()};

View file

@ -10,6 +10,7 @@
#include <QLabel> #include <QLabel>
#include <QMenuBar> #include <QMenuBar>
#include "cmppart.h"
#include "exdpart.h" #include "exdpart.h"
#include "exlpart.h" #include "exlpart.h"
#include "filepropertieswindow.h" #include "filepropertieswindow.h"
@ -97,6 +98,10 @@ void MainWindow::refreshParts(QString path)
auto shpkWidget = new SHPKPart(data); auto shpkWidget = new SHPKPart(data);
shpkWidget->load(file); shpkWidget->load(file);
partHolder->addTab(shpkWidget, QStringLiteral("Shader Package")); partHolder->addTab(shpkWidget, QStringLiteral("Shader Package"));
} else if (info.completeSuffix() == QStringLiteral("cmp")) {
auto cmpWidget = new CmpPart(data);
cmpWidget->load(file);
partHolder->addTab(cmpWidget, QStringLiteral("Chara Make Params"));
} }
auto hexWidget = new HexPart(); auto hexWidget = new HexPart();