diff --git a/mdlviewer/include/fullmodelviewer.h b/mdlviewer/include/fullmodelviewer.h index 19875c2..d9a3045 100644 --- a/mdlviewer/include/fullmodelviewer.h +++ b/mdlviewer/include/fullmodelviewer.h @@ -14,19 +14,27 @@ public: Q_SIGNALS: void gearChanged(); -public Q_SLOTS: + public Q_SLOTS: void clear(); - void addGear(GearInfo& info); + void addGear(GearInfo &info); -private Q_SLOTS: + private Q_SLOTS: void reloadGear(); -private: + private: + void updateHeightScaling(float scale); + void updateBustScaling(float scale); + void updateCharacterParameters(); + std::optional topSlot; - std::optional bottomSlot;\ + std::optional bottomSlot; - GearView* gearView = nullptr; - QComboBox* raceCombo, *genderCombo; + GearView *gearView = nullptr; + QComboBox *raceCombo, *genderCombo; - GameData* data = nullptr; + GameData *data = nullptr; + physis_CMP cmp; + + float heightScale = 0.5f; + float bustScale = 0.5f; }; \ No newline at end of file diff --git a/mdlviewer/src/fullmodelviewer.cpp b/mdlviewer/src/fullmodelviewer.cpp index 2dd9c41..053c070 100644 --- a/mdlviewer/src/fullmodelviewer.cpp +++ b/mdlviewer/src/fullmodelviewer.cpp @@ -1,34 +1,68 @@ #include "fullmodelviewer.h" -#include "magic_enum.hpp" #include "boneeditor.h" +#include "magic_enum.hpp" +#include +#include #include FullModelViewer::FullModelViewer(GameData *data) : data(data) { - setWindowTitle("Full Model Viewer"); - setMinimumWidth(640); - setMinimumHeight(480); + setWindowTitle("Full Model Viewer"); + setMinimumWidth(640); + setMinimumHeight(480); - auto layout = new QVBoxLayout(); - setLayout(layout); + auto layout = new QVBoxLayout(); + setLayout(layout); - gearView = new GearView(data); + cmp = physis_cmp_parse( + physis_gamedata_extract_file(data, "chara/xls/charamake/human.cmp")); - auto viewportLayout = new QHBoxLayout(); - viewportLayout->addWidget(gearView, 1); - viewportLayout->addWidget(new BoneEditor(gearView)); - layout->addLayout(viewportLayout); + gearView = new GearView(data); + updateCharacterParameters(); - auto controlLayout = new QHBoxLayout(); - layout->addLayout(controlLayout); + connect(gearView, &GearView::modelReloaded, this, + &FullModelViewer::updateCharacterParameters); - raceCombo = new QComboBox(); - connect(raceCombo, qOverload(&QComboBox::currentIndexChanged), [this](int index) { - gearView->setRace((Race)index); - }); - controlLayout->addWidget(raceCombo); + auto viewportLayout = new QHBoxLayout(); + viewportLayout->addWidget(gearView, 1); + layout->addLayout(viewportLayout); - for (auto [race, race_name] : magic_enum::enum_entries()) { + auto characterEditorWidget = new QWidget(); + auto characterEditorLayout = new QFormLayout(); + characterEditorWidget->setLayout(characterEditorLayout); + + auto characterHeight = new QSlider(); + characterHeight->setOrientation(Qt::Horizontal); + characterHeight->setSliderPosition(50); + connect(characterHeight, &QSlider::sliderMoved, this, [this](int position) { + const float scale = (float)position / 100.0f; + updateHeightScaling(scale); + }); + characterEditorLayout->addRow("Height", characterHeight); + + auto bustSize = new QSlider(); + bustSize->setOrientation(Qt::Horizontal); + bustSize->setSliderPosition(50); + connect(bustSize, &QSlider::sliderMoved, this, [this](int position) { + const float scale = (float)position / 100.0f; + updateBustScaling(scale); + }); + characterEditorLayout->addRow("Bust Size", bustSize); + + auto tabWidget = new QTabWidget(); + tabWidget->addTab(new BoneEditor(gearView), "Bone Editor"); + tabWidget->addTab(characterEditorWidget, "Character Editor"); + viewportLayout->addWidget(tabWidget); + + auto controlLayout = new QHBoxLayout(); + layout->addLayout(controlLayout); + + raceCombo = new QComboBox(); + connect(raceCombo, qOverload(&QComboBox::currentIndexChanged), + [this](int index) { gearView->setRace((Race)index); }); + controlLayout->addWidget(raceCombo); + + for (auto [race, race_name] : magic_enum::enum_entries()) { raceCombo->addItem(race_name.data()); } @@ -95,4 +129,63 @@ void FullModelViewer::reloadGear() { } } +void FullModelViewer::updateHeightScaling(float scale) { + auto &boneData = *gearView->part().skeleton; + for (int i = 0; i < boneData.num_bones; i++) { + const std::string_view name{boneData.bones[i].name}; + if (name == "n_root") { + auto racialScaling = physis_cmp_get_racial_scaling_parameters( + cmp, gearView->currentRace, gearView->currentSubrace); + + const float minSize = gearView->currentGender == Gender::Male + ? racialScaling.male_min_size + : racialScaling.female_min_size; + const float maxSize = gearView->currentGender == Gender::Male + ? racialScaling.male_max_size + : racialScaling.female_max_size; + + const float size = glm::mix(minSize, maxSize, scale); + + boneData.bones[i].scale[0] = size; + boneData.bones[i].scale[1] = size; + boneData.bones[i].scale[2] = size; + + gearView->part().reloadRenderer(); + } + } + + heightScale = scale; +} + +void FullModelViewer::updateBustScaling(float scale) { + auto &boneData = *gearView->part().skeleton; + for (int i = 0; i < boneData.num_bones; i++) { + const std::string_view name{boneData.bones[i].name}; + if (name == "j_mune_l" || name == "j_mune_r") { + auto racialScaling = physis_cmp_get_racial_scaling_parameters( + cmp, gearView->currentRace, gearView->currentSubrace); + + const float rangeX = glm::mix(racialScaling.bust_min_x, + racialScaling.bust_max_x, scale); + const float rangeY = glm::mix(racialScaling.bust_min_y, + racialScaling.bust_max_y, scale); + const float rangeZ = glm::mix(racialScaling.bust_min_z, + racialScaling.bust_max_z, scale); + + boneData.bones[i].scale[0] = rangeX; + boneData.bones[i].scale[1] = rangeY; + boneData.bones[i].scale[2] = rangeZ; + + gearView->part().reloadRenderer(); + } + } + + bustScale = scale; +} + +void FullModelViewer::updateCharacterParameters() { + updateHeightScaling(heightScale); + updateBustScaling(bustScale); +} + #include "moc_fullmodelviewer.cpp" \ No newline at end of file