diff --git a/armoury/CMakeLists.txt b/armoury/CMakeLists.txt index 8829fa0..efa0130 100644 --- a/armoury/CMakeLists.txt +++ b/armoury/CMakeLists.txt @@ -35,11 +35,11 @@ target_link_libraries(novus-armoury PUBLIC Qt6::Core Qt6::Widgets Qt6::Concurrent - magic_enum physis z physis-logger mdlpart + cmppart imgui novus-common) diff --git a/armoury/include/cmpeditor.h b/armoury/include/cmpeditor.h index a8bb339..5df6b3e 100644 --- a/armoury/include/cmpeditor.h +++ b/armoury/include/cmpeditor.h @@ -3,55 +3,12 @@ #pragma once -#include -#include -#include +#include "cmppart.h" -class RaceTreeData : public QObject -{ - Q_OBJECT - -public: - RaceTreeData(Race race, Subrace subrace) - : race(race) - , subrace(subrace) - { - } - - Race race; - Subrace subrace; -}; - -class CmpEditor : public QWidget +class CmpEditor : public CmpPart { Q_OBJECT public: 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; }; diff --git a/armoury/src/cmpeditor.cpp b/armoury/src/cmpeditor.cpp index 638ca05..6cc7dda 100644 --- a/armoury/src/cmpeditor.cpp +++ b/armoury/src/cmpeditor.cpp @@ -3,137 +3,12 @@ #include "cmpeditor.h" -#include -#include -#include -#include - -#include "magic_enum.hpp" - -// TODO: move this to physis -struct RaceTree { - Race baseRace; - std::vector subRaces; -}; - -std::vector 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) - : QWidget(parent) - , data(data) + : CmpPart(data) { setWindowTitle(QStringLiteral("CMP Editor")); - auto layout = new QHBoxLayout(); - 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(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); + load(physis_gamedata_extract_file(data, "chara/xls/charamake/human.cmp")); } #include "moc_cmpeditor.cpp" \ No newline at end of file diff --git a/parts/CMakeLists.txt b/parts/CMakeLists.txt index edb6ee1..2bb4cad 100644 --- a/parts/CMakeLists.txt +++ b/parts/CMakeLists.txt @@ -1,6 +1,7 @@ # SPDX-FileCopyrightText: 2023 Joshua Goins # SPDX-License-Identifier: CC0-1.0 +add_subdirectory(cmp) add_subdirectory(exd) add_subdirectory(exl) add_subdirectory(hex) diff --git a/parts/cmp/CMakeLists.txt b/parts/cmp/CMakeLists.txt new file mode 100644 index 0000000..a445cd7 --- /dev/null +++ b/parts/cmp/CMakeLists.txt @@ -0,0 +1,9 @@ +# SPDX-FileCopyrightText: 2023 Joshua Goins +# 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}) \ No newline at end of file diff --git a/parts/cmp/cmppart.cpp b/parts/cmp/cmppart.cpp new file mode 100644 index 0000000..5a37987 --- /dev/null +++ b/parts/cmp/cmppart.cpp @@ -0,0 +1,140 @@ +// SPDX-FileCopyrightText: 2023 Joshua Goins +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "cmppart.h" + +#include +#include +#include +#include + +#include "magic_enum.hpp" + +// TODO: move this to physis +struct RaceTree { + Race baseRace; + std::vector subRaces; +}; + +const std::vector 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(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" \ No newline at end of file diff --git a/parts/cmp/cmppart.h b/parts/cmp/cmppart.h new file mode 100644 index 0000000..bc8f913 --- /dev/null +++ b/parts/cmp/cmppart.h @@ -0,0 +1,62 @@ +// SPDX-FileCopyrightText: 2023 Joshua Goins +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include +#include +#include + +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; +}; \ No newline at end of file diff --git a/sagasu/CMakeLists.txt b/sagasu/CMakeLists.txt index 84884df..455fb27 100644 --- a/sagasu/CMakeLists.txt +++ b/sagasu/CMakeLists.txt @@ -22,6 +22,6 @@ target_sources(novus-sagasu PRIVATE src/filepropertieswindow.cpp src/filetreemodel.cpp) 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}) diff --git a/sagasu/src/indexer.cpp b/sagasu/src/indexer.cpp index fe4ccca..e41cc78 100644 --- a/sagasu/src/indexer.cpp +++ b/sagasu/src/indexer.cpp @@ -14,7 +14,8 @@ const std::array known_folders{"common", "chara/equipment/e0000/texture", "chara/human/c0101/obj/face/f0001/model", "shader/sm5/shpk", - "chara/xls/bonedeformer"}; + "chara/xls/bonedeformer", + "chara/xls/charamake"}; const std::array common_font{"common/VulgarWordsFilter.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("shader/sm5/shpk/character.shpk")); database.addFile(QStringLiteral("chara/xls/bonedeformer/human.pbd")); + database.addFile(QStringLiteral("chara/xls/charamake/human.cmp")); /*const QString gameDir{getGameDirectory()}; const std::string gameDirStd{gameDir.toStdString()}; diff --git a/sagasu/src/mainwindow.cpp b/sagasu/src/mainwindow.cpp index 205aed2..ebb70a1 100644 --- a/sagasu/src/mainwindow.cpp +++ b/sagasu/src/mainwindow.cpp @@ -10,6 +10,7 @@ #include #include +#include "cmppart.h" #include "exdpart.h" #include "exlpart.h" #include "filepropertieswindow.h" @@ -97,6 +98,10 @@ void MainWindow::refreshParts(QString path) auto shpkWidget = new SHPKPart(data); shpkWidget->load(file); 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();