mirror of
https://github.com/redstrate/Novus.git
synced 2025-04-23 12:37:45 +00:00
Make armoury's CMP editor a general-purpose part, add it to sagasu
This commit is contained in:
parent
70cbd9672c
commit
6caedba0d9
10 changed files with 226 additions and 175 deletions
|
@ -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)
|
||||
|
||||
|
|
|
@ -3,55 +3,12 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QWidget>
|
||||
#include <physis.hpp>
|
||||
#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;
|
||||
};
|
||||
|
|
|
@ -3,137 +3,12 @@
|
|||
|
||||
#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)
|
||||
: 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<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);
|
||||
load(physis_gamedata_extract_file(data, "chara/xls/charamake/human.cmp"));
|
||||
}
|
||||
|
||||
#include "moc_cmpeditor.cpp"
|
|
@ -1,6 +1,7 @@
|
|||
# SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
add_subdirectory(cmp)
|
||||
add_subdirectory(exd)
|
||||
add_subdirectory(exl)
|
||||
add_subdirectory(hex)
|
||||
|
|
9
parts/cmp/CMakeLists.txt
Normal file
9
parts/cmp/CMakeLists.txt
Normal 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
140
parts/cmp/cmppart.cpp
Normal 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
62
parts/cmp/cmppart.h
Normal 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;
|
||||
};
|
|
@ -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})
|
||||
|
|
|
@ -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()};
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <QLabel>
|
||||
#include <QMenuBar>
|
||||
|
||||
#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();
|
||||
|
|
Loading…
Add table
Reference in a new issue