1
Fork 0
mirror of https://github.com/redstrate/Novus.git synced 2025-04-25 05:17:44 +00:00

Add editable character parameters to FMV

This commit is contained in:
Joshua Goins 2023-07-07 16:02:45 -04:00
parent a9d121d885
commit 97dda3d091
2 changed files with 128 additions and 27 deletions

View file

@ -14,19 +14,27 @@ public:
Q_SIGNALS: Q_SIGNALS:
void gearChanged(); void gearChanged();
public Q_SLOTS: public Q_SLOTS:
void clear(); void clear();
void addGear(GearInfo& info); void addGear(GearInfo &info);
private Q_SLOTS: private Q_SLOTS:
void reloadGear(); void reloadGear();
private: private:
void updateHeightScaling(float scale);
void updateBustScaling(float scale);
void updateCharacterParameters();
std::optional<GearInfo> topSlot; std::optional<GearInfo> topSlot;
std::optional<GearInfo> bottomSlot;\ std::optional<GearInfo> bottomSlot;
GearView* gearView = nullptr; GearView *gearView = nullptr;
QComboBox* raceCombo, *genderCombo; QComboBox *raceCombo, *genderCombo;
GameData* data = nullptr; GameData *data = nullptr;
physis_CMP cmp;
float heightScale = 0.5f;
float bustScale = 0.5f;
}; };

View file

@ -1,34 +1,68 @@
#include "fullmodelviewer.h" #include "fullmodelviewer.h"
#include "magic_enum.hpp"
#include "boneeditor.h" #include "boneeditor.h"
#include "magic_enum.hpp"
#include <QFormLayout>
#include <QGroupBox>
#include <QVBoxLayout> #include <QVBoxLayout>
FullModelViewer::FullModelViewer(GameData *data) : data(data) { FullModelViewer::FullModelViewer(GameData *data) : data(data) {
setWindowTitle("Full Model Viewer"); setWindowTitle("Full Model Viewer");
setMinimumWidth(640); setMinimumWidth(640);
setMinimumHeight(480); setMinimumHeight(480);
auto layout = new QVBoxLayout(); auto layout = new QVBoxLayout();
setLayout(layout); setLayout(layout);
gearView = new GearView(data); cmp = physis_cmp_parse(
physis_gamedata_extract_file(data, "chara/xls/charamake/human.cmp"));
auto viewportLayout = new QHBoxLayout(); gearView = new GearView(data);
viewportLayout->addWidget(gearView, 1); updateCharacterParameters();
viewportLayout->addWidget(new BoneEditor(gearView));
layout->addLayout(viewportLayout);
auto controlLayout = new QHBoxLayout(); connect(gearView, &GearView::modelReloaded, this,
layout->addLayout(controlLayout); &FullModelViewer::updateCharacterParameters);
raceCombo = new QComboBox(); auto viewportLayout = new QHBoxLayout();
connect(raceCombo, qOverload<int>(&QComboBox::currentIndexChanged), [this](int index) { viewportLayout->addWidget(gearView, 1);
gearView->setRace((Race)index); layout->addLayout(viewportLayout);
});
controlLayout->addWidget(raceCombo);
for (auto [race, race_name] : magic_enum::enum_entries<Race>()) { 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<int>(&QComboBox::currentIndexChanged),
[this](int index) { gearView->setRace((Race)index); });
controlLayout->addWidget(raceCombo);
for (auto [race, race_name] : magic_enum::enum_entries<Race>()) {
raceCombo->addItem(race_name.data()); 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" #include "moc_fullmodelviewer.cpp"