mirror of
https://github.com/redstrate/Novus.git
synced 2025-06-07 21:57:46 +00:00
Move gar loading to a different thread and prevent unnecessary updates
This commit is contained in:
parent
e67011ad71
commit
d8d890bfbb
9 changed files with 254 additions and 129 deletions
|
@ -18,6 +18,7 @@ public:
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void gearChanged();
|
void gearChanged();
|
||||||
|
void loadingChanged(bool loading);
|
||||||
|
|
||||||
public Q_SLOTS:
|
public Q_SLOTS:
|
||||||
void clear();
|
void clear();
|
||||||
|
|
|
@ -24,6 +24,11 @@ struct GearInfo {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline bool operator==(const GearInfo &a, const GearInfo &b)
|
||||||
|
{
|
||||||
|
return a.name == b.name && a.slot == b.slot;
|
||||||
|
}
|
||||||
|
|
||||||
struct GameData;
|
struct GameData;
|
||||||
|
|
||||||
class GearView : public QWidget {
|
class GearView : public QWidget {
|
||||||
|
@ -51,6 +56,7 @@ public:
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void gearChanged();
|
void gearChanged();
|
||||||
void modelReloaded();
|
void modelReloaded();
|
||||||
|
void loadingChanged(bool loading);
|
||||||
|
|
||||||
void raceChanged();
|
void raceChanged();
|
||||||
void subraceChanged();
|
void subraceChanged();
|
||||||
|
@ -63,8 +69,8 @@ Q_SIGNALS:
|
||||||
void tailChanged();
|
void tailChanged();
|
||||||
|
|
||||||
public Q_SLOTS:
|
public Q_SLOTS:
|
||||||
void clear();
|
|
||||||
void addGear(GearInfo& gear);
|
void addGear(GearInfo& gear);
|
||||||
|
void removeGear(GearInfo &gear);
|
||||||
|
|
||||||
void setRace(Race race);
|
void setRace(Race race);
|
||||||
void setSubrace(Subrace subrace);
|
void setSubrace(Subrace subrace);
|
||||||
|
@ -76,7 +82,6 @@ public Q_SLOTS:
|
||||||
void setEar(int bodyVer);
|
void setEar(int bodyVer);
|
||||||
void setTail(int bodyVer);
|
void setTail(int bodyVer);
|
||||||
|
|
||||||
void reloadModel();
|
|
||||||
void reloadRaceDeforms();
|
void reloadRaceDeforms();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -84,11 +89,29 @@ private:
|
||||||
|
|
||||||
uint32_t maxLod = 0;
|
uint32_t maxLod = 0;
|
||||||
|
|
||||||
std::vector<GearInfo> gears;
|
struct LoadedGear {
|
||||||
|
GearInfo info;
|
||||||
|
physis_MDL mdl;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<LoadedGear> loadedGears;
|
||||||
|
std::vector<LoadedGear> queuedGearAdditions;
|
||||||
|
std::vector<LoadedGear> queuedGearRemovals;
|
||||||
|
bool gearDirty = false;
|
||||||
|
|
||||||
std::optional<int> face = 1, hair = 1, ear = 1, tail;
|
std::optional<int> face = 1, hair = 1, ear = 1, tail;
|
||||||
|
bool faceDirty = false, hairDirty = false, earDirty = false, tailDirty = false;
|
||||||
|
bool raceDirty = false;
|
||||||
|
|
||||||
MDLPart* mdlPart = nullptr;
|
MDLPart* mdlPart = nullptr;
|
||||||
|
|
||||||
GameData* data;
|
GameData* data;
|
||||||
FileCache& cache;
|
FileCache& cache;
|
||||||
|
|
||||||
|
bool updating = false;
|
||||||
|
void updatePart();
|
||||||
|
bool needsUpdate() const;
|
||||||
|
|
||||||
|
void gearUpdate(LoadedGear &gear);
|
||||||
|
void queueGearUpdate(LoadedGear &gear);
|
||||||
};
|
};
|
|
@ -34,6 +34,8 @@ public Q_SLOTS:
|
||||||
void setGender(Gender gender);
|
void setGender(Gender gender);
|
||||||
void setLevelOfDetail(int lod);
|
void setLevelOfDetail(int lod);
|
||||||
|
|
||||||
|
void setFMVAvailable(bool available);
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void reloadGear();
|
void reloadGear();
|
||||||
|
|
||||||
|
@ -50,6 +52,7 @@ private:
|
||||||
QPushButton *addToFMVButton, *exportButton;
|
QPushButton *addToFMVButton, *exportButton;
|
||||||
|
|
||||||
bool loadingComboData = false;
|
bool loadingComboData = false;
|
||||||
|
bool fmvAvailable = false;
|
||||||
|
|
||||||
GameData* data = nullptr;
|
GameData* data = nullptr;
|
||||||
};
|
};
|
|
@ -124,6 +124,13 @@ FullModelViewer::FullModelViewer(GameData* data, FileCache& cache) : data(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
connect(this, &FullModelViewer::gearChanged, this, &FullModelViewer::reloadGear);
|
connect(this, &FullModelViewer::gearChanged, this, &FullModelViewer::reloadGear);
|
||||||
|
connect(gearView, &GearView::loadingChanged, this, &FullModelViewer::loadingChanged);
|
||||||
|
connect(this, &FullModelViewer::loadingChanged, this, [this, tabWidget](const bool loading) {
|
||||||
|
raceCombo->setEnabled(!loading);
|
||||||
|
subraceCombo->setEnabled(!loading);
|
||||||
|
genderCombo->setEnabled(!loading);
|
||||||
|
tabWidget->setEnabled(!loading);
|
||||||
|
});
|
||||||
|
|
||||||
reloadGear();
|
reloadGear();
|
||||||
}
|
}
|
||||||
|
@ -138,10 +145,14 @@ void FullModelViewer::clear() {
|
||||||
void FullModelViewer::addGear(GearInfo& info) {
|
void FullModelViewer::addGear(GearInfo& info) {
|
||||||
switch (info.slot) {
|
switch (info.slot) {
|
||||||
case Slot::Body:
|
case Slot::Body:
|
||||||
|
if (topSlot ? *topSlot != info : true) {
|
||||||
topSlot = info;
|
topSlot = info;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case Slot::Legs:
|
case Slot::Legs:
|
||||||
|
if (bottomSlot ? *bottomSlot != info : true) {
|
||||||
bottomSlot = info;
|
bottomSlot = info;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
|
@ -150,9 +161,8 @@ void FullModelViewer::addGear(GearInfo& info) {
|
||||||
Q_EMIT gearChanged();
|
Q_EMIT gearChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FullModelViewer::reloadGear() {
|
void FullModelViewer::reloadGear()
|
||||||
gearView->clear();
|
{
|
||||||
|
|
||||||
if (topSlot.has_value()) {
|
if (topSlot.has_value()) {
|
||||||
gearView->addGear(*topSlot);
|
gearView->addGear(*topSlot);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -4,7 +4,9 @@
|
||||||
#include "gearview.h"
|
#include "gearview.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QThreadPool>
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
|
#include <QtConcurrent>
|
||||||
|
|
||||||
#include "filecache.h"
|
#include "filecache.h"
|
||||||
#include "magic_enum.hpp"
|
#include "magic_enum.hpp"
|
||||||
|
@ -18,36 +20,30 @@ GearView::GearView(GameData* data, FileCache& cache) : data(data), cache(cache)
|
||||||
layout->addWidget(mdlPart);
|
layout->addWidget(mdlPart);
|
||||||
setLayout(layout);
|
setLayout(layout);
|
||||||
|
|
||||||
connect(this, &GearView::gearChanged, this, [=] {
|
mdlPart->requestUpdate = [this] {
|
||||||
reloadModel();
|
if (updating) {
|
||||||
});
|
return;
|
||||||
connect(this, &GearView::raceChanged, this, [=] {
|
}
|
||||||
reloadRaceDeforms();
|
|
||||||
reloadModel();
|
|
||||||
});
|
|
||||||
connect(this, &GearView::subraceChanged, this, [=] {
|
|
||||||
reloadRaceDeforms();
|
|
||||||
reloadModel();
|
|
||||||
});
|
|
||||||
connect(this, &GearView::genderChanged, this, [=] {
|
|
||||||
reloadRaceDeforms();
|
|
||||||
reloadModel();
|
|
||||||
});
|
|
||||||
connect(this, &GearView::levelOfDetailChanged, this, &GearView::reloadModel);
|
|
||||||
|
|
||||||
connect(this, &GearView::faceChanged, this, &GearView::reloadModel);
|
if (needsUpdate()) {
|
||||||
connect(this, &GearView::hairChanged, this, &GearView::reloadModel);
|
updating = true;
|
||||||
connect(this, &GearView::earChanged, this, &GearView::reloadModel);
|
|
||||||
connect(this, &GearView::tailChanged, this, &GearView::reloadModel);
|
Q_EMIT loadingChanged(true);
|
||||||
|
|
||||||
|
QtConcurrent::run(QThreadPool::globalInstance(), [this] {
|
||||||
|
updatePart();
|
||||||
|
Q_EMIT loadingChanged(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::pair<Race, Subrace>> GearView::supportedRaces() const {
|
std::vector<std::pair<Race, Subrace>> GearView::supportedRaces() const {
|
||||||
std::vector<std::pair<Race, Subrace>> races;
|
std::vector<std::pair<Race, Subrace>> races;
|
||||||
for (const auto& gear : gears) {
|
for (const auto &gear : loadedGears) {
|
||||||
for (auto [race, race_name] : magic_enum::enum_entries<Race>()) {
|
for (auto [race, race_name] : magic_enum::enum_entries<Race>()) {
|
||||||
for (auto subrace : physis_get_supported_subraces(race).subraces) {
|
for (auto subrace : physis_get_supported_subraces(race).subraces) {
|
||||||
auto equip_path =
|
auto equip_path = physis_build_equipment_path(gear.info.modelInfo.primaryID, race, subrace, currentGender, gear.info.slot);
|
||||||
physis_build_equipment_path(gear.modelInfo.primaryID, race, subrace, currentGender, gear.slot);
|
|
||||||
|
|
||||||
if (cache.fileExists(QLatin1String(equip_path)))
|
if (cache.fileExists(QLatin1String(equip_path)))
|
||||||
races.emplace_back(race, subrace);
|
races.emplace_back(race, subrace);
|
||||||
|
@ -60,10 +56,9 @@ std::vector<std::pair<Race, Subrace>> GearView::supportedRaces() const {
|
||||||
|
|
||||||
std::vector<Gender> GearView::supportedGenders() const {
|
std::vector<Gender> GearView::supportedGenders() const {
|
||||||
std::vector<Gender> genders;
|
std::vector<Gender> genders;
|
||||||
for (const auto& gear : gears) {
|
for (const auto &gear : loadedGears) {
|
||||||
for (auto [gender, gender_name] : magic_enum::enum_entries<Gender>()) {
|
for (auto [gender, gender_name] : magic_enum::enum_entries<Gender>()) {
|
||||||
auto equip_path = physis_build_equipment_path(
|
auto equip_path = physis_build_equipment_path(gear.info.modelInfo.primaryID, currentRace, Subrace::Midlander, currentGender, gear.info.slot);
|
||||||
gear.modelInfo.primaryID, currentRace, Subrace::Midlander, currentGender, gear.slot);
|
|
||||||
|
|
||||||
if (cache.fileExists(QLatin1String(equip_path)))
|
if (cache.fileExists(QLatin1String(equip_path)))
|
||||||
genders.push_back(gender);
|
genders.push_back(gender);
|
||||||
|
@ -81,16 +76,22 @@ void GearView::exportModel(const QString& fileName) {
|
||||||
mdlPart->exportModel(fileName);
|
mdlPart->exportModel(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GearView::clear() {
|
void GearView::addGear(GearInfo &gear)
|
||||||
gears.clear();
|
{
|
||||||
|
qDebug() << "Adding gear" << gear.name.c_str();
|
||||||
|
|
||||||
|
queuedGearAdditions.emplace_back(gear);
|
||||||
|
gearDirty = true;
|
||||||
|
|
||||||
Q_EMIT gearChanged();
|
Q_EMIT gearChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GearView::addGear(GearInfo& gear) {
|
void GearView::removeGear(GearInfo &gear)
|
||||||
qDebug() << "Adding gear" << gear.name.c_str();
|
{
|
||||||
|
qDebug() << "Removing gear" << gear.name.c_str();
|
||||||
|
|
||||||
gears.push_back(gear);
|
queuedGearRemovals.emplace_back(gear);
|
||||||
|
gearDirty = true;
|
||||||
|
|
||||||
Q_EMIT gearChanged();
|
Q_EMIT gearChanged();
|
||||||
}
|
}
|
||||||
|
@ -102,9 +103,8 @@ void GearView::setRace(Race race) {
|
||||||
|
|
||||||
currentRace = race;
|
currentRace = race;
|
||||||
|
|
||||||
auto supportedSubraces = physis_get_supported_subraces(race);
|
const auto supportedSubraces = physis_get_supported_subraces(race);
|
||||||
if (supportedSubraces.subraces[0] == currentSubrace || supportedSubraces.subraces[1] == currentSubrace) {
|
if (supportedSubraces.subraces[0] != currentSubrace && supportedSubraces.subraces[1] != currentSubrace) {
|
||||||
} else {
|
|
||||||
setSubrace(supportedSubraces.subraces[0]);
|
setSubrace(supportedSubraces.subraces[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,6 +114,8 @@ void GearView::setRace(Race race) {
|
||||||
setTail(-1);
|
setTail(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
raceDirty = true;
|
||||||
|
|
||||||
Q_EMIT raceChanged();
|
Q_EMIT raceChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,6 +125,12 @@ void GearView::setSubrace(Subrace subrace) {
|
||||||
}
|
}
|
||||||
|
|
||||||
currentSubrace = subrace;
|
currentSubrace = subrace;
|
||||||
|
|
||||||
|
// Hyur is the only race that has two different subraces
|
||||||
|
if (currentRace == Race::Hyur) {
|
||||||
|
raceDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
Q_EMIT subraceChanged();
|
Q_EMIT subraceChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,6 +140,9 @@ void GearView::setGender(Gender gender) {
|
||||||
}
|
}
|
||||||
|
|
||||||
currentGender = gender;
|
currentGender = gender;
|
||||||
|
|
||||||
|
raceDirty = true;
|
||||||
|
|
||||||
Q_EMIT genderChanged();
|
Q_EMIT genderChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,115 +152,173 @@ void GearView::setLevelOfDetail(int lod) {
|
||||||
}
|
}
|
||||||
|
|
||||||
currentLod = lod;
|
currentLod = lod;
|
||||||
|
|
||||||
Q_EMIT levelOfDetailChanged();
|
Q_EMIT levelOfDetailChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GearView::setFace(int bodyVer) {
|
void GearView::setFace(const int faceCode)
|
||||||
if (face == bodyVer) {
|
{
|
||||||
|
if (face == faceCode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bodyVer == -1) {
|
if (faceCode == -1) {
|
||||||
face = std::nullopt;
|
face = std::nullopt;
|
||||||
} else {
|
} else {
|
||||||
face = bodyVer;
|
face = faceCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
faceDirty = true;
|
||||||
Q_EMIT faceChanged();
|
Q_EMIT faceChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GearView::setHair(int bodyVer) {
|
void GearView::setHair(int hairCode)
|
||||||
if (hair == bodyVer) {
|
{
|
||||||
|
if (hair == hairCode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bodyVer == -1) {
|
if (hairCode == -1) {
|
||||||
hair = std::nullopt;
|
hair = std::nullopt;
|
||||||
} else {
|
} else {
|
||||||
hair = bodyVer;
|
hair = hairCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hairDirty = true;
|
||||||
Q_EMIT hairChanged();
|
Q_EMIT hairChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GearView::setEar(int bodyVer) {
|
void GearView::setEar(const int earCode)
|
||||||
if (ear == bodyVer) {
|
{
|
||||||
|
if (ear == earCode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bodyVer == -1) {
|
if (earCode == -1) {
|
||||||
ear = std::nullopt;
|
ear = std::nullopt;
|
||||||
} else {
|
} else {
|
||||||
ear = bodyVer;
|
ear = earCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
earDirty = true;
|
||||||
Q_EMIT earChanged();
|
Q_EMIT earChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GearView::setTail(int bodyVer) {
|
void GearView::setTail(const int tailCode)
|
||||||
if (tail == bodyVer) {
|
{
|
||||||
|
if (tail == tailCode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bodyVer == -1) {
|
if (tailCode == -1) {
|
||||||
tail = std::nullopt;
|
tail = std::nullopt;
|
||||||
} else {
|
} else {
|
||||||
tail = bodyVer;
|
tail = tailCode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tailDirty = true;
|
||||||
Q_EMIT tailChanged();
|
Q_EMIT tailChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GearView::reloadModel() {
|
void GearView::reloadRaceDeforms()
|
||||||
mdlPart->clear();
|
{
|
||||||
|
qDebug() << "Loading race deform matrices for " << magic_enum::enum_name(currentRace).data() << magic_enum::enum_name(currentSubrace).data()
|
||||||
|
<< magic_enum::enum_name(currentGender).data();
|
||||||
|
const int raceCode = physis_get_race_code(currentRace, currentSubrace, currentGender);
|
||||||
|
qDebug() << "Race code: " << raceCode;
|
||||||
|
|
||||||
maxLod = 0;
|
QString skelName = QString{"c%1b0001.skel"}.arg(raceCode, 4, 10, QLatin1Char{'0'});
|
||||||
|
mdlPart->setSkeleton(physis_skeleton_from_skel(physis_read_file(skelName.toStdString().c_str())));
|
||||||
|
|
||||||
for (const auto& gear : gears) {
|
// racial deforms don't work on Hyur Midlander, not needed? TODO not sure
|
||||||
auto mdl_data = cache.lookupFile(physis_build_equipment_path(
|
if (currentSubrace != Subrace::Midlander) {
|
||||||
gear.modelInfo.primaryID, currentRace, currentSubrace, currentGender, gear.slot));
|
QString deformName = QString{"c%1_deform.json"}.arg(raceCode, 4, 10, QLatin1Char{'0'});
|
||||||
|
mdlPart->loadRaceDeformMatrices(physis_read_file(deformName.toStdString().c_str()));
|
||||||
// attempt to load the next best race
|
} else {
|
||||||
// currently hardcoded to hyur midlander
|
for (auto &data : mdlPart->boneData) {
|
||||||
Race fallbackRace = currentRace;
|
data.deformRaceMatrix = glm::mat4(1.0f);
|
||||||
Subrace fallbackSubrace = currentSubrace;
|
|
||||||
if (mdl_data.size == 0) {
|
|
||||||
mdl_data = cache.lookupFile(physis_build_equipment_path(
|
|
||||||
gear.modelInfo.primaryID, Race::Hyur, Subrace::Midlander, currentGender, gear.slot));
|
|
||||||
fallbackRace = Race::Hyur;
|
|
||||||
fallbackSubrace = Subrace::Midlander;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (mdl_data.size > 0) {
|
MDLPart &GearView::part() const
|
||||||
auto mdl = physis_mdl_parse(mdl_data.size, mdl_data.data);
|
{
|
||||||
|
return *mdlPart;
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<physis_Material> materials;
|
void GearView::updatePart()
|
||||||
for (int i = 0; i < mdl.num_material_names; i++) {
|
{
|
||||||
const char* material_name = mdl.material_names[i];
|
qInfo() << raceDirty << gearDirty << updating;
|
||||||
|
if (raceDirty) {
|
||||||
|
// if race changes, all of the models need to be reloaded.
|
||||||
|
// TODO: in the future, we can be a bit smarter about this, lots of races use the same model (hyur)
|
||||||
|
for (auto &part : loadedGears) {
|
||||||
|
mdlPart->removeModel(part.mdl);
|
||||||
|
}
|
||||||
|
queuedGearAdditions = loadedGears;
|
||||||
|
loadedGears.clear();
|
||||||
|
gearDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
const std::string mtrl_path = gear.getMtrlPath(material_name);
|
if (gearDirty) {
|
||||||
const std::string skinmtrl_path = physis_build_skin_material_path(physis_get_race_code(fallbackRace, fallbackSubrace, currentGender), 1, material_name);
|
for (auto &gearAddition : queuedGearAdditions) {
|
||||||
|
auto mdl_data = cache.lookupFile(
|
||||||
|
physis_build_equipment_path(gearAddition.info.modelInfo.primaryID, currentRace, currentSubrace, currentGender, gearAddition.info.slot));
|
||||||
|
|
||||||
if (cache.fileExists(QLatin1String(mtrl_path.c_str()))) {
|
// attempt to load the next best race
|
||||||
auto mat = physis_material_parse(cache.lookupFile(mtrl_path.c_str()));
|
// currently hardcoded to hyur midlander
|
||||||
materials.push_back(mat);
|
Race fallbackRace = currentRace;
|
||||||
}
|
Subrace fallbackSubrace = currentSubrace;
|
||||||
|
if (mdl_data.size == 0) {
|
||||||
if (cache.fileExists(QLatin1String(skinmtrl_path.c_str()))) {
|
mdl_data = cache.lookupFile(
|
||||||
auto mat = physis_material_parse(cache.lookupFile(skinmtrl_path.c_str()));
|
physis_build_equipment_path(gearAddition.info.modelInfo.primaryID, Race::Hyur, Subrace::Midlander, currentGender, gearAddition.info.slot));
|
||||||
materials.push_back(mat);
|
fallbackRace = Race::Hyur;
|
||||||
}
|
fallbackSubrace = Subrace::Midlander;
|
||||||
}
|
}
|
||||||
|
|
||||||
maxLod = std::max(mdl.num_lod, maxLod);
|
if (mdl_data.size > 0) {
|
||||||
|
auto mdl = physis_mdl_parse(mdl_data.size, mdl_data.data);
|
||||||
|
|
||||||
mdlPart->addModel(mdl, materials, currentLod);
|
std::vector<physis_Material> materials;
|
||||||
|
for (int i = 0; i < mdl.num_material_names; i++) {
|
||||||
|
const char *material_name = mdl.material_names[i];
|
||||||
|
|
||||||
|
const std::string mtrl_path = gearAddition.info.getMtrlPath(material_name);
|
||||||
|
const std::string skinmtrl_path =
|
||||||
|
physis_build_skin_material_path(physis_get_race_code(fallbackRace, fallbackSubrace, currentGender), 1, material_name);
|
||||||
|
|
||||||
|
if (cache.fileExists(QLatin1String(mtrl_path.c_str()))) {
|
||||||
|
auto mat = physis_material_parse(cache.lookupFile(mtrl_path.c_str()));
|
||||||
|
materials.push_back(mat);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cache.fileExists(QLatin1String(skinmtrl_path.c_str()))) {
|
||||||
|
auto mat = physis_material_parse(cache.lookupFile(skinmtrl_path.c_str()));
|
||||||
|
materials.push_back(mat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
maxLod = std::max(mdl.num_lod, maxLod);
|
||||||
|
|
||||||
|
mdlPart->addModel(mdl, materials, currentLod);
|
||||||
|
gearAddition.mdl = mdl;
|
||||||
|
loadedGears.push_back(gearAddition);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &queuedRemoval : queuedGearRemovals) {
|
||||||
|
mdlPart->removeModel(queuedRemoval.mdl);
|
||||||
|
loadedGears.erase(std::remove_if(loadedGears.begin(),
|
||||||
|
loadedGears.end(),
|
||||||
|
[queuedRemoval](const LoadedGear other) {
|
||||||
|
return queuedRemoval.info == other.info;
|
||||||
|
}),
|
||||||
|
loadedGears.end());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (face) {
|
if (face) {
|
||||||
auto mdl_data = cache.lookupFile(physis_build_character_path(
|
auto mdl_data = cache.lookupFile(physis_build_character_path(CharacterCategory::Face, *face, currentRace, currentSubrace, currentGender));
|
||||||
CharacterCategory::Face, *face, currentRace, currentSubrace, currentGender));
|
|
||||||
|
|
||||||
if (mdl_data.size > 0) {
|
if (mdl_data.size > 0) {
|
||||||
auto mdl = physis_mdl_parse(mdl_data.size, mdl_data.data);
|
auto mdl = physis_mdl_parse(mdl_data.size, mdl_data.data);
|
||||||
|
@ -270,8 +339,7 @@ void GearView::reloadModel() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hair) {
|
if (hair) {
|
||||||
auto mdl_data = cache.lookupFile(physis_build_character_path(
|
auto mdl_data = cache.lookupFile(physis_build_character_path(CharacterCategory::Hair, *hair, currentRace, currentSubrace, currentGender));
|
||||||
CharacterCategory::Hair, *hair, currentRace, currentSubrace, currentGender));
|
|
||||||
|
|
||||||
if (mdl_data.size > 0) {
|
if (mdl_data.size > 0) {
|
||||||
auto mdl = physis_mdl_parse(mdl_data.size, mdl_data.data);
|
auto mdl = physis_mdl_parse(mdl_data.size, mdl_data.data);
|
||||||
|
@ -292,8 +360,7 @@ void GearView::reloadModel() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ear) {
|
if (ear) {
|
||||||
auto mdl_data = cache.lookupFile(physis_build_character_path(
|
auto mdl_data = cache.lookupFile(physis_build_character_path(CharacterCategory::Hair, *ear, currentRace, currentSubrace, currentGender));
|
||||||
CharacterCategory::Hair, *ear, currentRace, currentSubrace, currentGender));
|
|
||||||
|
|
||||||
if (mdl_data.size > 0) {
|
if (mdl_data.size > 0) {
|
||||||
auto mdl = physis_mdl_parse(mdl_data.size, mdl_data.data);
|
auto mdl = physis_mdl_parse(mdl_data.size, mdl_data.data);
|
||||||
|
@ -314,8 +381,7 @@ void GearView::reloadModel() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tail) {
|
if (tail) {
|
||||||
auto mdl_data = cache.lookupFile(physis_build_character_path(
|
auto mdl_data = cache.lookupFile(physis_build_character_path(CharacterCategory::Tail, *tail, currentRace, currentSubrace, currentGender));
|
||||||
CharacterCategory::Tail, *tail, currentRace, currentSubrace, currentGender));
|
|
||||||
|
|
||||||
if (mdl_data.size > 0) {
|
if (mdl_data.size > 0) {
|
||||||
auto mdl = physis_mdl_parse(mdl_data.size, mdl_data.data);
|
auto mdl = physis_mdl_parse(mdl_data.size, mdl_data.data);
|
||||||
|
@ -330,31 +396,18 @@ void GearView::reloadModel() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Q_EMIT modelReloaded();
|
raceDirty = false;
|
||||||
|
gearDirty = false;
|
||||||
|
updating = false;
|
||||||
|
faceDirty = false;
|
||||||
|
hairDirty = false;
|
||||||
|
earDirty = false;
|
||||||
|
tailDirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GearView::reloadRaceDeforms() {
|
bool GearView::needsUpdate() const
|
||||||
qDebug() << "Loading race deform matrices for " << magic_enum::enum_name(currentRace).data()
|
{
|
||||||
<< magic_enum::enum_name(currentSubrace).data() << magic_enum::enum_name(currentGender).data();
|
return gearDirty || raceDirty || faceDirty || hairDirty || earDirty || tailDirty;
|
||||||
const int raceCode = physis_get_race_code(currentRace, currentSubrace, currentGender);
|
|
||||||
qDebug() << "Race code: " << raceCode;
|
|
||||||
|
|
||||||
QString skelName = QString{"c%1b0001.skel"}.arg(raceCode, 4, 10, QLatin1Char{'0'});
|
|
||||||
mdlPart->setSkeleton(physis_skeleton_from_skel(physis_read_file(skelName.toStdString().c_str())));
|
|
||||||
|
|
||||||
// racial deforms don't work on Hyur Midlander, not needed? TODO not sure
|
|
||||||
if (currentSubrace != Subrace::Midlander) {
|
|
||||||
QString deformName = QString{"c%1_deform.json"}.arg(raceCode, 4, 10, QLatin1Char{'0'});
|
|
||||||
mdlPart->loadRaceDeformMatrices(physis_read_file(deformName.toStdString().c_str()));
|
|
||||||
} else {
|
|
||||||
for (auto& data : mdlPart->boneData) {
|
|
||||||
data.deformRaceMatrix = glm::mat4(1.0f);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MDLPart& GearView::part() const {
|
|
||||||
return *mdlPart;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "moc_gearview.cpp"
|
#include "moc_gearview.cpp"
|
||||||
|
|
|
@ -83,6 +83,9 @@ MainWindow::MainWindow(GameData* in_data) : data(*in_data), cache(FileCache{*in_
|
||||||
connect(gearView, &SingleGearView::addToFullModelViewer, this, [=](GearInfo& info) {
|
connect(gearView, &SingleGearView::addToFullModelViewer, this, [=](GearInfo& info) {
|
||||||
fullModelViewer->addGear(info);
|
fullModelViewer->addGear(info);
|
||||||
});
|
});
|
||||||
|
connect(fullModelViewer, &FullModelViewer::loadingChanged, this, [=](const bool loading) {
|
||||||
|
gearView->setFMVAvailable(!loading);
|
||||||
|
});
|
||||||
layout->addWidget(gearView);
|
layout->addWidget(gearView);
|
||||||
|
|
||||||
fullModelViewer = new FullModelViewer(&data, cache);
|
fullModelViewer = new FullModelViewer(&data, cache);
|
||||||
|
|
|
@ -100,15 +100,25 @@ SingleGearView::SingleGearView(GameData* data, FileCache& cache) : data(data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void SingleGearView::clear() {
|
void SingleGearView::clear() {
|
||||||
|
if (currentGear) {
|
||||||
|
gearView->removeGear(*currentGear);
|
||||||
|
}
|
||||||
currentGear.reset();
|
currentGear.reset();
|
||||||
|
|
||||||
Q_EMIT gearChanged();
|
Q_EMIT gearChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SingleGearView::setGear(const GearInfo& info) {
|
void SingleGearView::setGear(const GearInfo& info) {
|
||||||
currentGear = info;
|
if (info != currentGear) {
|
||||||
|
if (currentGear) {
|
||||||
|
gearView->removeGear(*currentGear);
|
||||||
|
}
|
||||||
|
|
||||||
Q_EMIT gearChanged();
|
currentGear = info;
|
||||||
|
gearView->addGear(*currentGear);
|
||||||
|
|
||||||
|
Q_EMIT gearChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SingleGearView::setRace(Race race) {
|
void SingleGearView::setRace(Race race) {
|
||||||
|
@ -147,19 +157,16 @@ void SingleGearView::setLevelOfDetail(int lod) {
|
||||||
Q_EMIT levelOfDetailChanged();
|
Q_EMIT levelOfDetailChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void SingleGearView::reloadGear() {
|
void SingleGearView::reloadGear()
|
||||||
gearView->clear();
|
{
|
||||||
|
|
||||||
raceCombo->setEnabled(currentGear.has_value());
|
raceCombo->setEnabled(currentGear.has_value());
|
||||||
subraceCombo->setEnabled(currentGear.has_value());
|
subraceCombo->setEnabled(currentGear.has_value());
|
||||||
genderCombo->setEnabled(currentGear.has_value());
|
genderCombo->setEnabled(currentGear.has_value());
|
||||||
lodCombo->setEnabled(currentGear.has_value());
|
lodCombo->setEnabled(currentGear.has_value());
|
||||||
addToFMVButton->setEnabled(currentGear.has_value());
|
addToFMVButton->setEnabled(currentGear.has_value() && fmvAvailable);
|
||||||
exportButton->setEnabled(currentGear.has_value());
|
exportButton->setEnabled(currentGear.has_value());
|
||||||
|
|
||||||
if (currentGear.has_value()) {
|
if (currentGear.has_value()) {
|
||||||
gearView->addGear(*currentGear);
|
|
||||||
|
|
||||||
loadingComboData = true;
|
loadingComboData = true;
|
||||||
|
|
||||||
const auto oldRace = static_cast<Race>(raceCombo->itemData(raceCombo->currentIndex()).toInt());
|
const auto oldRace = static_cast<Race>(raceCombo->itemData(raceCombo->currentIndex()).toInt());
|
||||||
|
@ -212,4 +219,12 @@ void SingleGearView::reloadGear() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SingleGearView::setFMVAvailable(const bool available)
|
||||||
|
{
|
||||||
|
if (fmvAvailable != available) {
|
||||||
|
fmvAvailable = available;
|
||||||
|
addToFMVButton->setEnabled(currentGear.has_value() && available);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#include "moc_singlegearview.cpp"
|
#include "moc_singlegearview.cpp"
|
|
@ -116,6 +116,9 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void render() {
|
void render() {
|
||||||
|
if (part->requestUpdate)
|
||||||
|
part->requestUpdate();
|
||||||
|
|
||||||
glm::vec3 position(
|
glm::vec3 position(
|
||||||
part->cameraDistance * sin(part->yaw),
|
part->cameraDistance * sin(part->yaw),
|
||||||
part->cameraDistance * part->pitch,
|
part->cameraDistance * part->pitch,
|
||||||
|
@ -554,4 +557,14 @@ void MDLPart::calculateBone(physis_Skeleton& skeleton, physis_Bone& bone, const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MDLPart::removeModel(const physis_MDL &mdl)
|
||||||
|
{
|
||||||
|
models.erase(std::remove_if(models.begin(),
|
||||||
|
models.end(),
|
||||||
|
[mdl](const RenderModel other) {
|
||||||
|
return mdl.lods == other.model.lods;
|
||||||
|
}),
|
||||||
|
models.end());
|
||||||
|
}
|
||||||
|
|
||||||
#include "moc_mdlpart.cpp"
|
#include "moc_mdlpart.cpp"
|
|
@ -47,6 +47,8 @@ public:
|
||||||
|
|
||||||
std::vector<BoneData> boneData;
|
std::vector<BoneData> boneData;
|
||||||
|
|
||||||
|
std::function<void()> requestUpdate;
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void modelChanged();
|
void modelChanged();
|
||||||
void skeletonChanged();
|
void skeletonChanged();
|
||||||
|
@ -58,6 +60,8 @@ public Q_SLOTS:
|
||||||
/// Adds a new MDL with a list of materials used.
|
/// Adds a new MDL with a list of materials used.
|
||||||
void addModel(physis_MDL mdl, std::vector<physis_Material> materials, int lod);
|
void addModel(physis_MDL mdl, std::vector<physis_Material> materials, int lod);
|
||||||
|
|
||||||
|
void removeModel(const physis_MDL &mdl);
|
||||||
|
|
||||||
/// Sets the skeleton any skinned MDLs should bind to.
|
/// Sets the skeleton any skinned MDLs should bind to.
|
||||||
void setSkeleton(physis_Skeleton skeleton);
|
void setSkeleton(physis_Skeleton skeleton);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue