2023-08-06 08:48:11 -04:00
|
|
|
// SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
|
|
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
|
2023-04-09 15:31:19 -04:00
|
|
|
#include "gearview.h"
|
|
|
|
|
2023-09-25 23:48:03 -04:00
|
|
|
#include <QThreadPool>
|
2023-07-07 16:16:21 -04:00
|
|
|
#include <QVBoxLayout>
|
2023-09-25 23:48:03 -04:00
|
|
|
#include <QtConcurrent>
|
2023-09-26 17:09:12 -04:00
|
|
|
#include <imgui.h>
|
2023-04-09 15:31:19 -04:00
|
|
|
|
2023-07-09 10:54:27 -04:00
|
|
|
#include "filecache.h"
|
2023-08-06 08:48:11 -04:00
|
|
|
#include "magic_enum.hpp"
|
2023-07-09 10:54:27 -04:00
|
|
|
|
2023-10-12 23:44:48 -04:00
|
|
|
GearView::GearView(GameData *data, FileCache &cache, QWidget *parent)
|
2024-02-04 14:00:46 -05:00
|
|
|
: QFrame(parent)
|
2023-10-12 23:44:48 -04:00
|
|
|
, data(data)
|
|
|
|
, cache(cache)
|
|
|
|
{
|
2024-02-04 14:00:46 -05:00
|
|
|
setFrameShape(QFrame::Shape::Panel);
|
|
|
|
setFrameShadow(QFrame::Shadow::Sunken);
|
2023-04-09 15:31:19 -04:00
|
|
|
|
2024-02-04 14:00:46 -05:00
|
|
|
mdlPart = new MDLPart(data, cache);
|
2023-07-07 16:01:39 -04:00
|
|
|
reloadRaceDeforms();
|
2023-07-07 16:16:21 -04:00
|
|
|
|
|
|
|
auto layout = new QVBoxLayout();
|
2023-09-26 20:21:06 -04:00
|
|
|
layout->setContentsMargins(0, 0, 0, 0);
|
2023-07-07 16:16:21 -04:00
|
|
|
layout->addWidget(mdlPart);
|
|
|
|
setLayout(layout);
|
|
|
|
|
2023-09-25 23:48:03 -04:00
|
|
|
mdlPart->requestUpdate = [this] {
|
2023-09-26 17:21:07 -04:00
|
|
|
auto &io = ImGui::GetIO();
|
|
|
|
if (updating) {
|
|
|
|
if (ImGui::Begin("Loading", nullptr, ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoInputs)) {
|
|
|
|
ImGui::SetWindowPos(ImVec2(0, 0));
|
|
|
|
ImGui::SetWindowSize(io.DisplaySize);
|
|
|
|
|
2024-02-04 15:13:46 -05:00
|
|
|
// TODO: localize
|
2023-09-26 17:21:07 -04:00
|
|
|
const char *loadingLabel{"Loading gear..."};
|
|
|
|
ImGui::SetCursorPosX((io.DisplaySize.x - ImGui::CalcTextSize(loadingLabel).x) * 0.5f);
|
|
|
|
ImGui::SetCursorPosY((io.DisplaySize.y - ImGui::CalcTextSize(loadingLabel).y) * 0.5f);
|
|
|
|
ImGui::Text("%s", loadingLabel);
|
|
|
|
}
|
|
|
|
ImGui::End();
|
|
|
|
}
|
2023-09-26 17:09:12 -04:00
|
|
|
|
2023-09-25 23:48:03 -04:00
|
|
|
if (updating) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (needsUpdate()) {
|
|
|
|
updating = true;
|
|
|
|
|
|
|
|
Q_EMIT loadingChanged(true);
|
|
|
|
|
2023-12-09 22:35:59 -05:00
|
|
|
Q_UNUSED(QtConcurrent::run(QThreadPool::globalInstance(), [this] {
|
2023-09-25 23:48:03 -04:00
|
|
|
updatePart();
|
|
|
|
Q_EMIT loadingChanged(false);
|
2023-12-10 08:39:45 -05:00
|
|
|
}))
|
2023-09-25 23:48:03 -04:00
|
|
|
}
|
|
|
|
};
|
2023-04-09 15:31:19 -04:00
|
|
|
}
|
|
|
|
|
2023-10-12 23:44:48 -04:00
|
|
|
std::vector<std::pair<Race, Subrace>> GearView::supportedRaces() const
|
|
|
|
{
|
2023-07-07 16:16:21 -04:00
|
|
|
std::vector<std::pair<Race, Subrace>> races;
|
2023-09-25 23:48:03 -04:00
|
|
|
for (const auto &gear : loadedGears) {
|
2023-12-09 22:35:59 -05:00
|
|
|
for (const auto &[race, race_name] : magic_enum::enum_entries<Race>()) {
|
2023-09-26 19:48:59 -04:00
|
|
|
for (const auto subrace : physis_get_supported_subraces(race).subraces) {
|
2023-09-25 23:48:03 -04:00
|
|
|
auto equip_path = physis_build_equipment_path(gear.info.modelInfo.primaryID, race, subrace, currentGender, gear.info.slot);
|
2023-07-07 16:16:21 -04:00
|
|
|
|
2023-09-25 22:03:33 -04:00
|
|
|
if (cache.fileExists(QLatin1String(equip_path)))
|
2023-07-07 16:16:21 -04:00
|
|
|
races.emplace_back(race, subrace);
|
|
|
|
}
|
|
|
|
}
|
2023-04-09 15:31:19 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return races;
|
|
|
|
}
|
|
|
|
|
2023-10-12 23:44:48 -04:00
|
|
|
std::vector<Gender> GearView::supportedGenders() const
|
|
|
|
{
|
2023-04-09 15:31:19 -04:00
|
|
|
std::vector<Gender> genders;
|
2023-09-25 23:48:03 -04:00
|
|
|
for (const auto &gear : loadedGears) {
|
2023-07-07 16:16:21 -04:00
|
|
|
for (auto [gender, gender_name] : magic_enum::enum_entries<Gender>()) {
|
2023-09-25 23:48:03 -04:00
|
|
|
auto equip_path = physis_build_equipment_path(gear.info.modelInfo.primaryID, currentRace, Subrace::Midlander, currentGender, gear.info.slot);
|
2023-04-09 15:31:19 -04:00
|
|
|
|
2023-09-25 22:03:33 -04:00
|
|
|
if (cache.fileExists(QLatin1String(equip_path)))
|
2023-04-09 15:31:19 -04:00
|
|
|
genders.push_back(gender);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return genders;
|
|
|
|
}
|
|
|
|
|
2023-10-12 23:44:48 -04:00
|
|
|
int GearView::lodCount() const
|
|
|
|
{
|
2023-04-09 15:31:19 -04:00
|
|
|
return maxLod;
|
|
|
|
}
|
|
|
|
|
2023-10-12 23:44:48 -04:00
|
|
|
void GearView::exportModel(const QString &fileName)
|
|
|
|
{
|
2023-04-09 15:31:19 -04:00
|
|
|
mdlPart->exportModel(fileName);
|
|
|
|
}
|
|
|
|
|
2023-09-25 23:48:03 -04:00
|
|
|
void GearView::addGear(GearInfo &gear)
|
|
|
|
{
|
|
|
|
qDebug() << "Adding gear" << gear.name.c_str();
|
|
|
|
|
|
|
|
queuedGearAdditions.emplace_back(gear);
|
2024-04-30 15:27:50 -04:00
|
|
|
|
|
|
|
for (auto loadedGear : loadedGears) {
|
|
|
|
if (loadedGear.info.slot == gear.slot) {
|
|
|
|
queuedGearRemovals.push_back(loadedGear);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-25 23:48:03 -04:00
|
|
|
gearDirty = true;
|
2023-04-09 15:31:19 -04:00
|
|
|
|
|
|
|
Q_EMIT gearChanged();
|
|
|
|
}
|
|
|
|
|
2023-09-25 23:48:03 -04:00
|
|
|
void GearView::removeGear(GearInfo &gear)
|
|
|
|
{
|
|
|
|
qDebug() << "Removing gear" << gear.name.c_str();
|
2023-04-09 15:31:19 -04:00
|
|
|
|
2023-09-25 23:48:03 -04:00
|
|
|
queuedGearRemovals.emplace_back(gear);
|
|
|
|
gearDirty = true;
|
2023-04-09 15:31:19 -04:00
|
|
|
|
|
|
|
Q_EMIT gearChanged();
|
|
|
|
}
|
|
|
|
|
2023-10-12 23:44:48 -04:00
|
|
|
void GearView::setRace(Race race)
|
|
|
|
{
|
2023-04-09 15:31:19 -04:00
|
|
|
if (currentRace == race) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
currentRace = race;
|
2023-07-07 16:01:39 -04:00
|
|
|
|
2023-09-25 23:48:03 -04:00
|
|
|
const auto supportedSubraces = physis_get_supported_subraces(race);
|
|
|
|
if (supportedSubraces.subraces[0] != currentSubrace && supportedSubraces.subraces[1] != currentSubrace) {
|
2023-07-07 16:01:39 -04:00
|
|
|
setSubrace(supportedSubraces.subraces[0]);
|
|
|
|
}
|
|
|
|
|
2023-07-08 11:58:38 -04:00
|
|
|
if (race == Race::AuRa || race == Race::Miqote) {
|
|
|
|
setTail(1);
|
|
|
|
} else {
|
|
|
|
setTail(-1);
|
|
|
|
}
|
|
|
|
|
2023-09-25 23:48:03 -04:00
|
|
|
raceDirty = true;
|
|
|
|
|
2023-04-09 15:31:19 -04:00
|
|
|
Q_EMIT raceChanged();
|
|
|
|
}
|
|
|
|
|
2023-10-12 23:44:48 -04:00
|
|
|
void GearView::setSubrace(Subrace subrace)
|
|
|
|
{
|
2023-07-07 16:01:39 -04:00
|
|
|
if (currentSubrace == subrace) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
currentSubrace = subrace;
|
2023-09-25 23:48:03 -04:00
|
|
|
|
|
|
|
// Hyur is the only race that has two different subraces
|
|
|
|
if (currentRace == Race::Hyur) {
|
|
|
|
raceDirty = true;
|
|
|
|
}
|
|
|
|
|
2023-07-07 16:01:39 -04:00
|
|
|
Q_EMIT subraceChanged();
|
|
|
|
}
|
|
|
|
|
2023-10-12 23:44:48 -04:00
|
|
|
void GearView::setGender(Gender gender)
|
|
|
|
{
|
2023-04-09 15:31:19 -04:00
|
|
|
if (currentGender == gender) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
currentGender = gender;
|
2023-09-25 23:48:03 -04:00
|
|
|
|
|
|
|
raceDirty = true;
|
|
|
|
|
2023-04-09 15:31:19 -04:00
|
|
|
Q_EMIT genderChanged();
|
|
|
|
}
|
|
|
|
|
2023-10-12 23:44:48 -04:00
|
|
|
void GearView::setLevelOfDetail(int lod)
|
|
|
|
{
|
2023-04-09 15:31:19 -04:00
|
|
|
if (currentLod == lod) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
currentLod = lod;
|
2023-09-25 23:48:03 -04:00
|
|
|
|
2024-01-29 21:58:17 -05:00
|
|
|
// TODO: maybe should be gearDirty?
|
|
|
|
raceDirty = true;
|
|
|
|
|
2023-04-09 15:31:19 -04:00
|
|
|
Q_EMIT levelOfDetailChanged();
|
|
|
|
}
|
|
|
|
|
2023-09-25 23:48:03 -04:00
|
|
|
void GearView::setFace(const int faceCode)
|
|
|
|
{
|
|
|
|
if (face == faceCode) {
|
2023-07-08 11:58:38 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-09-25 23:48:03 -04:00
|
|
|
if (faceCode == -1) {
|
2023-07-08 11:58:38 -04:00
|
|
|
face = std::nullopt;
|
|
|
|
} else {
|
2023-09-25 23:48:03 -04:00
|
|
|
face = faceCode;
|
2023-07-08 11:58:38 -04:00
|
|
|
}
|
|
|
|
|
2023-09-25 23:48:03 -04:00
|
|
|
faceDirty = true;
|
2023-07-08 11:58:38 -04:00
|
|
|
Q_EMIT faceChanged();
|
|
|
|
}
|
|
|
|
|
2023-09-25 23:48:03 -04:00
|
|
|
void GearView::setHair(int hairCode)
|
|
|
|
{
|
|
|
|
if (hair == hairCode) {
|
2023-07-08 11:58:38 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-09-25 23:48:03 -04:00
|
|
|
if (hairCode == -1) {
|
2023-07-08 11:58:38 -04:00
|
|
|
hair = std::nullopt;
|
|
|
|
} else {
|
2023-09-25 23:48:03 -04:00
|
|
|
hair = hairCode;
|
2023-07-08 11:58:38 -04:00
|
|
|
}
|
|
|
|
|
2023-09-25 23:48:03 -04:00
|
|
|
hairDirty = true;
|
2023-07-08 11:58:38 -04:00
|
|
|
Q_EMIT hairChanged();
|
|
|
|
}
|
|
|
|
|
2023-09-25 23:48:03 -04:00
|
|
|
void GearView::setEar(const int earCode)
|
|
|
|
{
|
|
|
|
if (ear == earCode) {
|
2023-07-08 11:58:38 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-09-25 23:48:03 -04:00
|
|
|
if (earCode == -1) {
|
2023-07-08 11:58:38 -04:00
|
|
|
ear = std::nullopt;
|
|
|
|
} else {
|
2023-09-25 23:48:03 -04:00
|
|
|
ear = earCode;
|
2023-07-08 11:58:38 -04:00
|
|
|
}
|
|
|
|
|
2023-09-25 23:48:03 -04:00
|
|
|
earDirty = true;
|
2023-07-08 11:58:38 -04:00
|
|
|
Q_EMIT earChanged();
|
|
|
|
}
|
|
|
|
|
2023-09-25 23:48:03 -04:00
|
|
|
void GearView::setTail(const int tailCode)
|
|
|
|
{
|
|
|
|
if (tail == tailCode) {
|
2023-07-08 11:58:38 -04:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-09-25 23:48:03 -04:00
|
|
|
if (tailCode == -1) {
|
2023-07-08 11:58:38 -04:00
|
|
|
tail = std::nullopt;
|
|
|
|
} else {
|
2023-09-25 23:48:03 -04:00
|
|
|
tail = tailCode;
|
2023-07-08 11:58:38 -04:00
|
|
|
}
|
|
|
|
|
2023-09-25 23:48:03 -04:00
|
|
|
tailDirty = true;
|
2023-07-08 11:58:38 -04:00
|
|
|
Q_EMIT tailChanged();
|
|
|
|
}
|
|
|
|
|
2023-09-25 23:48:03 -04:00
|
|
|
void GearView::reloadRaceDeforms()
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
|
2023-10-13 15:03:17 -04:00
|
|
|
QString skelName = QStringLiteral("chara/human/c%1/skeleton/base/b0001/skl_c%1b0001.sklb").arg(raceCode, 4, 10, QLatin1Char{'0'});
|
|
|
|
std::string skelNameStd = skelName.toStdString();
|
|
|
|
mdlPart->setSkeleton(physis_parse_skeleton(physis_gamedata_extract_file(data, skelNameStd.c_str())));
|
2023-09-25 23:48:03 -04:00
|
|
|
}
|
2023-04-09 15:31:19 -04:00
|
|
|
|
2023-09-25 23:48:03 -04:00
|
|
|
MDLPart &GearView::part() const
|
|
|
|
{
|
|
|
|
return *mdlPart;
|
|
|
|
}
|
2023-07-07 16:01:39 -04:00
|
|
|
|
2023-09-25 23:48:03 -04:00
|
|
|
void GearView::updatePart()
|
|
|
|
{
|
|
|
|
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)
|
2024-04-30 15:21:23 -04:00
|
|
|
mdlPart->clear();
|
2023-09-25 23:48:03 -04:00
|
|
|
queuedGearAdditions = loadedGears;
|
|
|
|
loadedGears.clear();
|
|
|
|
gearDirty = true;
|
|
|
|
}
|
2023-07-07 16:01:39 -04:00
|
|
|
|
2023-09-26 21:26:19 -04:00
|
|
|
const auto sanitizeMdlPath = [](const QLatin1String mdlPath) -> QString {
|
|
|
|
return QString(mdlPath).section(QLatin1Char('/'), -1).remove(QStringLiteral(".mdl"));
|
|
|
|
};
|
|
|
|
|
2023-09-25 23:48:03 -04:00
|
|
|
if (gearDirty) {
|
|
|
|
for (auto &gearAddition : queuedGearAdditions) {
|
2023-09-26 21:21:04 -04:00
|
|
|
auto mdlPath = QLatin1String(
|
2023-09-26 20:21:06 -04:00
|
|
|
physis_build_equipment_path(gearAddition.info.modelInfo.primaryID, currentRace, currentSubrace, currentGender, gearAddition.info.slot));
|
|
|
|
|
2023-09-26 19:48:59 -04:00
|
|
|
qInfo() << "Looking up" << magic_enum::enum_name(currentRace) << magic_enum::enum_name(currentSubrace) << magic_enum::enum_name(currentGender);
|
2023-09-26 20:21:06 -04:00
|
|
|
auto mdl_data = cache.lookupFile(mdlPath);
|
2023-09-25 23:48:03 -04:00
|
|
|
|
|
|
|
// attempt to load the next best race
|
|
|
|
// currently hardcoded to hyur midlander
|
|
|
|
Race fallbackRace = currentRace;
|
|
|
|
Subrace fallbackSubrace = currentSubrace;
|
|
|
|
if (mdl_data.size == 0) {
|
2023-09-26 20:21:06 -04:00
|
|
|
mdlPath = QLatin1String(
|
|
|
|
physis_build_equipment_path(gearAddition.info.modelInfo.primaryID, Race::Hyur, Subrace::Midlander, currentGender, gearAddition.info.slot));
|
|
|
|
mdl_data = cache.lookupFile(mdlPath);
|
2023-09-25 23:48:03 -04:00
|
|
|
fallbackRace = Race::Hyur;
|
|
|
|
fallbackSubrace = Subrace::Midlander;
|
|
|
|
}
|
2023-04-09 15:31:19 -04:00
|
|
|
|
2023-10-13 15:03:17 -04:00
|
|
|
if (fallbackRace != currentRace) {
|
|
|
|
qInfo() << "Fell back to hyur race for" << mdlPath;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fallbackSubrace != currentSubrace) {
|
|
|
|
qInfo() << "Fell back to midlander subrace for" << mdlPath;
|
|
|
|
}
|
|
|
|
|
2023-09-25 23:48:03 -04:00
|
|
|
if (mdl_data.size > 0) {
|
2023-10-13 17:02:29 -04:00
|
|
|
auto mdl = physis_mdl_parse(mdl_data);
|
2024-04-18 17:53:21 -04:00
|
|
|
if (mdl.p_ptr != nullptr) {
|
|
|
|
std::vector<physis_Material> materials;
|
|
|
|
for (uint32_t 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(QLatin1String(mtrl_path.c_str())));
|
|
|
|
materials.push_back(mat);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cache.fileExists(QLatin1String(skinmtrl_path.c_str()))) {
|
|
|
|
auto mat = physis_material_parse(cache.lookupFile(QLatin1String(skinmtrl_path.c_str())));
|
|
|
|
materials.push_back(mat);
|
|
|
|
}
|
2023-09-25 23:48:03 -04:00
|
|
|
}
|
|
|
|
|
2024-04-18 17:53:21 -04:00
|
|
|
maxLod = std::max(mdl.num_lod, maxLod);
|
|
|
|
|
|
|
|
gearAddition.bodyId = physis_get_race_code(fallbackRace, fallbackSubrace, currentGender);
|
|
|
|
mdlPart->addModel(mdl,
|
|
|
|
true,
|
|
|
|
glm::vec3(),
|
|
|
|
sanitizeMdlPath(mdlPath),
|
|
|
|
materials,
|
|
|
|
currentLod,
|
|
|
|
physis_get_race_code(currentRace, currentSubrace, currentGender),
|
|
|
|
gearAddition.bodyId);
|
|
|
|
gearAddition.mdl = mdl;
|
|
|
|
gearAddition.path = mdlPath;
|
|
|
|
loadedGears.push_back(gearAddition);
|
2023-04-09 15:31:19 -04:00
|
|
|
}
|
2023-09-25 23:48:03 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto &queuedRemoval : queuedGearRemovals) {
|
2023-12-09 15:55:33 -05:00
|
|
|
auto it = std::find_if(loadedGears.cbegin(), loadedGears.cend(), [queuedRemoval](const LoadedGear &other) {
|
|
|
|
return queuedRemoval.info == other.info;
|
|
|
|
});
|
|
|
|
|
|
|
|
if (it != loadedGears.cend()) {
|
|
|
|
mdlPart->removeModel((*it).mdl);
|
|
|
|
loadedGears.erase(std::remove_if(loadedGears.begin(),
|
|
|
|
loadedGears.end(),
|
|
|
|
[queuedRemoval](const LoadedGear &other) {
|
|
|
|
return queuedRemoval.info == other.info;
|
|
|
|
}),
|
|
|
|
loadedGears.end());
|
|
|
|
}
|
2023-04-09 15:31:19 -04:00
|
|
|
}
|
2023-12-09 15:55:33 -05:00
|
|
|
|
|
|
|
queuedGearAdditions.clear();
|
|
|
|
queuedGearRemovals.clear();
|
2023-04-09 15:31:19 -04:00
|
|
|
}
|
2023-07-06 17:38:19 -04:00
|
|
|
|
2024-04-30 15:21:23 -04:00
|
|
|
const auto loadBodyPart = [this, &sanitizeMdlPath](int index, CharacterCategory category, auto build_material_path_func) {
|
|
|
|
const auto mdlPath = QLatin1String(physis_build_character_path(category, index, currentRace, currentSubrace, currentGender));
|
2023-09-26 21:21:04 -04:00
|
|
|
auto mdl_data = cache.lookupFile(mdlPath);
|
2023-07-08 11:58:38 -04:00
|
|
|
|
|
|
|
if (mdl_data.size > 0) {
|
2023-10-13 17:02:29 -04:00
|
|
|
auto mdl = physis_mdl_parse(mdl_data);
|
2024-04-18 17:53:21 -04:00
|
|
|
if (mdl.p_ptr != nullptr) {
|
|
|
|
std::vector<physis_Material> materials;
|
|
|
|
for (uint32_t i = 0; i < mdl.num_material_names; i++) {
|
|
|
|
const char *material_name = mdl.material_names[i];
|
|
|
|
const std::string skinmtrl_path =
|
2024-04-30 15:21:23 -04:00
|
|
|
build_material_path_func(physis_get_race_code(currentRace, currentSubrace, currentGender), index, material_name);
|
2023-07-08 11:58:38 -04:00
|
|
|
|
2024-04-18 17:53:21 -04:00
|
|
|
if (cache.fileExists(QLatin1String(skinmtrl_path.c_str()))) {
|
|
|
|
auto mat = physis_material_parse(cache.lookupFile(QLatin1String(skinmtrl_path.c_str())));
|
|
|
|
materials.push_back(mat);
|
|
|
|
}
|
2023-07-08 11:58:38 -04:00
|
|
|
}
|
|
|
|
|
2024-04-18 17:53:21 -04:00
|
|
|
mdlPart->addModel(mdl, true, glm::vec3(), sanitizeMdlPath(mdlPath), materials, currentLod);
|
|
|
|
}
|
2023-07-08 11:58:38 -04:00
|
|
|
}
|
2024-04-30 15:21:23 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
if (face) {
|
|
|
|
loadBodyPart(*face, CharacterCategory::Face, physis_build_face_material_path);
|
2023-07-08 11:58:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (hair) {
|
2024-04-30 15:21:23 -04:00
|
|
|
loadBodyPart(*hair, CharacterCategory::Hair, physis_build_hair_material_path);
|
2023-07-08 11:58:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (ear) {
|
2024-04-30 15:21:23 -04:00
|
|
|
loadBodyPart(*ear, CharacterCategory::Ear, physis_build_ear_material_path);
|
2023-07-08 11:58:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (tail) {
|
2024-04-30 15:21:23 -04:00
|
|
|
loadBodyPart(*tail, CharacterCategory::Tail, physis_build_tail_material_path);
|
2023-07-08 11:58:38 -04:00
|
|
|
}
|
|
|
|
|
2023-09-25 23:48:03 -04:00
|
|
|
raceDirty = false;
|
|
|
|
gearDirty = false;
|
|
|
|
updating = false;
|
|
|
|
faceDirty = false;
|
|
|
|
hairDirty = false;
|
|
|
|
earDirty = false;
|
|
|
|
tailDirty = false;
|
2023-07-06 17:38:19 -04:00
|
|
|
}
|
|
|
|
|
2023-09-25 23:48:03 -04:00
|
|
|
bool GearView::needsUpdate() const
|
|
|
|
{
|
|
|
|
return gearDirty || raceDirty || faceDirty || hairDirty || earDirty || tailDirty;
|
2023-07-07 16:16:21 -04:00
|
|
|
}
|
2023-07-07 16:01:39 -04:00
|
|
|
|
2023-09-26 20:21:06 -04:00
|
|
|
QString GearView::getLoadedGearPath() const
|
|
|
|
{
|
|
|
|
if (loadedGears.empty()) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
return loadedGears[0].path;
|
|
|
|
}
|
|
|
|
|
2024-02-04 14:54:37 -05:00
|
|
|
void GearView::changeEvent(QEvent *event)
|
|
|
|
{
|
|
|
|
switch (event->type()) {
|
2024-04-30 15:21:23 -04:00
|
|
|
case QEvent::EnabledChange:
|
2024-02-04 14:54:37 -05:00
|
|
|
mdlPart->setEnabled(isEnabled());
|
2024-04-30 15:21:23 -04:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
2024-02-04 14:54:37 -05:00
|
|
|
}
|
|
|
|
QFrame::changeEvent(event);
|
|
|
|
}
|
|
|
|
|
2023-04-09 15:31:19 -04:00
|
|
|
#include "moc_gearview.cpp"
|