diff --git a/mdlviewer/CMakeLists.txt b/mdlviewer/CMakeLists.txt index b126538..a38f1f5 100644 --- a/mdlviewer/CMakeLists.txt +++ b/mdlviewer/CMakeLists.txt @@ -11,7 +11,9 @@ add_executable(mdlviewer src/fullmodelviewer.cpp src/quaternionedit.cpp src/boneeditor.cpp - src/cmpeditor.cpp) + src/cmpeditor.cpp + src/gearlistwidget.cpp + src/gearlistmodel.cpp) target_include_directories(mdlviewer PUBLIC include) diff --git a/mdlviewer/include/gearlistmodel.h b/mdlviewer/include/gearlistmodel.h new file mode 100644 index 0000000..0b768e7 --- /dev/null +++ b/mdlviewer/include/gearlistmodel.h @@ -0,0 +1,45 @@ +#pragma once + +#include "gearview.h" +#include + +enum class TreeType { + Root, + Category, + Item +}; + +struct TreeInformation { + TreeType type; + std::optional slotType; + TreeInformation* parent = nullptr; + std::optional gear; + int row = 0; + + std::vector children; +}; + +class GearListModel : public QAbstractItemModel { + Q_OBJECT + +public: + explicit GearListModel(GameData* data); + + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + int columnCount(const QModelIndex& parent = QModelIndex()) const override; + + QModelIndex index(int row, int column = 0, const QModelIndex& parent = QModelIndex()) const override; + QModelIndex parent(const QModelIndex& child) const override; + + QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + + std::optional getGearFromIndex(const QModelIndex& index); + +private: + std::vector gears; + QStringList slotNames; + + GameData* gameData = nullptr; + TreeInformation* rootItem = nullptr; +}; \ No newline at end of file diff --git a/mdlviewer/include/gearlistwidget.h b/mdlviewer/include/gearlistwidget.h new file mode 100644 index 0000000..7ff8d5d --- /dev/null +++ b/mdlviewer/include/gearlistwidget.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include +#include + +#include "gearview.h" + +class GearListWidget : public QWidget { + Q_OBJECT + +public: + explicit GearListWidget(GameData* data, QWidget* parent = nullptr); + +Q_SIGNALS: + void gearSelected(const GearInfo& gear); + +private: + QTreeView* listWidget = nullptr; + + GameData* data = nullptr; +}; diff --git a/mdlviewer/include/mainwindow.h b/mdlviewer/include/mainwindow.h index 8072593..dba2ed0 100644 --- a/mdlviewer/include/mainwindow.h +++ b/mdlviewer/include/mainwindow.h @@ -17,8 +17,6 @@ public: explicit MainWindow(GameData* data); private: - std::vector gears; - SingleGearView* gearView = nullptr; FullModelViewer* fullModelViewer = nullptr; diff --git a/mdlviewer/include/singlegearview.h b/mdlviewer/include/singlegearview.h index 6372611..78d630c 100644 --- a/mdlviewer/include/singlegearview.h +++ b/mdlviewer/include/singlegearview.h @@ -23,7 +23,7 @@ Q_SIGNALS: public Q_SLOTS: void clear(); - void setGear(GearInfo& info); + void setGear(const GearInfo& info); void setRace(Race race); void setSubrace(Subrace subrace); diff --git a/mdlviewer/src/gearlistmodel.cpp b/mdlviewer/src/gearlistmodel.cpp new file mode 100644 index 0000000..4ac372a --- /dev/null +++ b/mdlviewer/src/gearlistmodel.cpp @@ -0,0 +1,164 @@ +#include "gearlistmodel.h" + +#include +#include + +GearListModel::GearListModel(GameData* data) : gameData(data), QAbstractItemModel() { + beginResetModel(); + // smallclothes body + { + GearInfo info = {}; + info.name = "Smallclothes Body"; + info.slot = Slot::Body; + + gears.push_back(info); + } + + // smallclothes legs + { + GearInfo info = {}; + info.name = "Smallclothes Legs"; + info.slot = Slot::Legs; + + gears.push_back(info); + } + + auto exh = physis_gamedata_read_excel_sheet_header(data, "Item"); + + for (int p = 0; p < exh->page_count; p++) { + auto exd = physis_gamedata_read_excel_sheet(data, "Item", exh, Language::English, p); + + for (int i = 0; i < exd.row_count; i++) { + const auto row = exd.row_data[i]; + auto primaryModel = row.column_data[47].u_int64._0; + auto secondaryModel = row.column_data[48].u_int64._0; + + int16_t parts[4]; + memcpy(parts, &primaryModel, sizeof(int16_t) * 4); + + GearInfo info = {}; + info.name = row.column_data[9].string._0; + info.slot = physis_slot_from_id(row.column_data[17].u_int8._0); + info.modelInfo.primaryID = parts[0]; + + gears.push_back(info); + } + } + + for (auto slotName : magic_enum::enum_names()) { + slotNames.push_back(slotName.data()); + } + endResetModel(); + + rootItem = new TreeInformation(); + rootItem->type = TreeType::Root; + + int i = 0; + for (auto slot : magic_enum::enum_values()) { + TreeInformation* categoryItem = new TreeInformation(); + categoryItem->type = TreeType::Category; + categoryItem->slotType = slot; + categoryItem->parent = rootItem; + categoryItem->row = i++; + rootItem->children.push_back(categoryItem); + + int j = 0; + for (auto gear : gears) { + if (gear.slot == slot) { + TreeInformation* item = new TreeInformation(); + item->type = TreeType::Item; + item->gear = gear; + item->parent = categoryItem; + item->row = j++; + categoryItem->children.push_back(item); + } + } + } +} + +int GearListModel::rowCount(const QModelIndex& parent) const { + TreeInformation* parentItem; + if (parent.column() > 0) + return 0; + + if (!parent.isValid()) + parentItem = rootItem; + else + parentItem = static_cast(parent.internalPointer()); + + return parentItem->children.size(); +} + +int GearListModel::columnCount(const QModelIndex& parent) const { + return 1; +} + +QModelIndex GearListModel::index(int row, int column, const QModelIndex& parent) const { + if (!hasIndex(row, column, parent)) + return QModelIndex(); + + TreeInformation* parentItem; + + if (!parent.isValid()) + parentItem = rootItem; + else + parentItem = static_cast(parent.internalPointer()); + + TreeInformation* childItem = parentItem->children[row]; + if (childItem) + return createIndex(row, column, childItem); + return QModelIndex(); +} + +QModelIndex GearListModel::parent(const QModelIndex& index) const { + if (!index.isValid()) + return QModelIndex(); + + TreeInformation* childItem = static_cast(index.internalPointer()); + TreeInformation* parentItem = childItem->parent; + + if (parentItem == rootItem) + return QModelIndex(); + + return createIndex(parentItem->row, 0, parentItem); +} + +QVariant GearListModel::data(const QModelIndex& index, int role) const { + if (!index.isValid()) + return {}; + if (!index.isValid()) + return QVariant(); + + if (role != Qt::DisplayRole) + return QVariant(); + + TreeInformation* item = static_cast(index.internalPointer()); + + if (item->type == TreeType::Category) { + return magic_enum::enum_name(*item->slotType).data(); + } else if (item->type == TreeType::Item) { + return item->gear->name.data(); + } + + return {}; +} + +QVariant GearListModel::headerData(int section, Qt::Orientation orientation, int role) const { + if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { + if (section == 0) { + return "Name"; + } + } + + return QAbstractItemModel::headerData(section, orientation, role); +} + +std::optional GearListModel::getGearFromIndex(const QModelIndex& index) { + TreeInformation* item = static_cast(index.internalPointer()); + if (item->type == TreeType::Item) { + return item->gear; + } + return {}; +} + +#include "moc_gearlistmodel.cpp" \ No newline at end of file diff --git a/mdlviewer/src/gearlistwidget.cpp b/mdlviewer/src/gearlistwidget.cpp new file mode 100644 index 0000000..4bd8506 --- /dev/null +++ b/mdlviewer/src/gearlistwidget.cpp @@ -0,0 +1,26 @@ +#include "gearlistwidget.h" + +#include +#include + +#include "gearlistmodel.h" + +GearListWidget::GearListWidget(GameData* data, QWidget* parent) : data(data) { + auto layout = new QVBoxLayout(); + setLayout(layout); + + auto model = new GearListModel(data); + + listWidget = new QTreeView(); + listWidget->setModel(model); + + connect(listWidget, &QTreeView::clicked, [this, model](const QModelIndex& item) { + if (auto gear = model->getGearFromIndex(item)) { + Q_EMIT gearSelected(*gear); + } + }); + + layout->addWidget(listWidget); +} + +#include "moc_gearlistwidget.cpp" \ No newline at end of file diff --git a/mdlviewer/src/mainwindow.cpp b/mdlviewer/src/mainwindow.cpp index dd420e2..ced0538 100644 --- a/mdlviewer/src/mainwindow.cpp +++ b/mdlviewer/src/mainwindow.cpp @@ -16,6 +16,7 @@ #include #include "cmpeditor.h" +#include "gearlistwidget.h" MainWindow::MainWindow(GameData* in_data) : data(*in_data) { setWindowTitle("mdlviewer"); @@ -52,50 +53,12 @@ MainWindow::MainWindow(GameData* in_data) : data(*in_data) { auto layout = new QHBoxLayout(); dummyWidget->setLayout(layout); - // smallclothes body - { - GearInfo info = {}; - info.name = "Smallclothes Body"; - info.slot = Slot::Body; - - gears.push_back(info); - } - - // smallclothes legs - { - GearInfo info = {}; - info.name = "Smallclothes Legs"; - info.slot = Slot::Legs; - - gears.push_back(info); - } - - auto exh = physis_gamedata_read_excel_sheet_header(&data, "Item"); - auto exd = physis_gamedata_read_excel_sheet(&data, "Item", exh, Language::English, 1); - - for (int i = 0; i < exd.row_count; i++) { - const auto row = exd.row_data[i]; - auto primaryModel = row.column_data[47].u_int64._0; - auto secondaryModel = row.column_data[48].u_int64._0; - - int16_t parts[4]; - memcpy(parts, &primaryModel, sizeof(int16_t) * 4); - - GearInfo info = {}; - info.name = row.column_data[9].string._0; - info.slot = physis_slot_from_id(row.column_data[17].u_int8._0); - info.modelInfo.primaryID = parts[0]; - - gears.push_back(info); - } - - auto listWidget = new QListWidget(); - for (auto gear : gears) - listWidget->addItem(gear.name.c_str()); - - listWidget->setMaximumWidth(200); - - layout->addWidget(listWidget); + auto gearListWidget = new GearListWidget(&data); + gearListWidget->setMaximumWidth(350); + connect(gearListWidget, &GearListWidget::gearSelected, this, [=](const GearInfo& gear) { + gearView->setGear(gear); + }); + layout->addWidget(gearListWidget); gearView = new SingleGearView(&data); connect(gearView, &SingleGearView::addToFullModelViewer, this, [=](GearInfo& info) { @@ -103,15 +66,6 @@ MainWindow::MainWindow(GameData* in_data) : data(*in_data) { }); layout->addWidget(gearView); - connect(listWidget, &QListWidget::itemClicked, [this](QListWidgetItem* item) { - for (auto& gear : gears) { - if (gear.name == item->text().toStdString()) { - gearView->setGear(gear); - return; - } - } - }); - fullModelViewer = new FullModelViewer(&data); fullModelViewer->show(); } \ No newline at end of file diff --git a/mdlviewer/src/singlegearview.cpp b/mdlviewer/src/singlegearview.cpp index 7e00a78..18ed8da 100644 --- a/mdlviewer/src/singlegearview.cpp +++ b/mdlviewer/src/singlegearview.cpp @@ -91,7 +91,7 @@ void SingleGearView::clear() { Q_EMIT gearChanged(); } -void SingleGearView::setGear(GearInfo& info) { +void SingleGearView::setGear(const GearInfo& info) { currentGear = info; Q_EMIT gearChanged();