From 01b3fc6ef270edbf351a58d390d4aab33fb44ca3 Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Sat, 8 Jul 2023 11:58:38 -0400 Subject: [PATCH] Add support for loading faces, hairs, ears and tails --- mdlviewer/include/gearview.h | 11 ++ mdlviewer/src/gearview.cpp | 188 +++++++++++++++++++++++++++++++++++ 2 files changed, 199 insertions(+) diff --git a/mdlviewer/include/gearview.h b/mdlviewer/include/gearview.h index 3571a06..473bc74 100644 --- a/mdlviewer/include/gearview.h +++ b/mdlviewer/include/gearview.h @@ -59,6 +59,11 @@ Q_SIGNALS: void genderChanged(); void levelOfDetailChanged(); + void faceChanged(); + void hairChanged(); + void earChanged(); + void tailChanged(); + public Q_SLOTS: void clear(); void addGear(GearInfo& gear); @@ -68,6 +73,11 @@ public Q_SLOTS: void setGender(Gender gender); void setLevelOfDetail(int lod); + void setFace(int bodyVer); + void setHair(int bodyVer); + void setEar(int bodyVer); + void setTail(int bodyVer); + void reloadModel(); void reloadRaceDeforms(); @@ -77,6 +87,7 @@ private: uint32_t maxLod = 0; std::vector gears; + std::optional face = 1, hair = 1, ear = 1, tail; MDLPart* mdlPart = nullptr; diff --git a/mdlviewer/src/gearview.cpp b/mdlviewer/src/gearview.cpp index b65376e..b9ba082 100644 --- a/mdlviewer/src/gearview.cpp +++ b/mdlviewer/src/gearview.cpp @@ -29,6 +29,11 @@ GearView::GearView(GameData* data) : data(data) { reloadModel(); }); connect(this, &GearView::levelOfDetailChanged, this, &GearView::reloadModel); + + connect(this, &GearView::faceChanged, this, &GearView::reloadModel); + connect(this, &GearView::hairChanged, this, &GearView::reloadModel); + connect(this, &GearView::earChanged, this, &GearView::reloadModel); + connect(this, &GearView::tailChanged, this, &GearView::reloadModel); } std::vector> GearView::supportedRaces() const { @@ -98,6 +103,12 @@ void GearView::setRace(Race race) { setSubrace(supportedSubraces.subraces[0]); } + if (race == Race::AuRa || race == Race::Miqote) { + setTail(1); + } else { + setTail(-1); + } + Q_EMIT raceChanged(); } @@ -128,6 +139,62 @@ void GearView::setLevelOfDetail(int lod) { Q_EMIT levelOfDetailChanged(); } +void GearView::setFace(int bodyVer) { + if (face == bodyVer) { + return; + } + + if (bodyVer == -1) { + face = std::nullopt; + } else { + face = bodyVer; + } + + Q_EMIT faceChanged(); +} + +void GearView::setHair(int bodyVer) { + if (hair == bodyVer) { + return; + } + + if (bodyVer == -1) { + hair = std::nullopt; + } else { + hair = bodyVer; + } + + Q_EMIT hairChanged(); +} + +void GearView::setEar(int bodyVer) { + if (ear == bodyVer) { + return; + } + + if (bodyVer == -1) { + ear = std::nullopt; + } else { + ear = bodyVer; + } + + Q_EMIT earChanged(); +} + +void GearView::setTail(int bodyVer) { + if (tail == bodyVer) { + return; + } + + if (bodyVer == -1) { + tail = std::nullopt; + } else { + tail = bodyVer; + } + + Q_EMIT tailChanged(); +} + void GearView::reloadModel() { mdlPart->clear(); @@ -195,6 +262,127 @@ void GearView::reloadModel() { } } + if (face) { + auto mdl_data = physis_gamedata_extract_file( + data, + physis_build_character_path( + CharacterCategory::Face, *face, currentRace, currentSubrace, currentGender)); + + if (mdl_data.size > 0) { + auto mdl = physis_mdl_parse(mdl_data.size, mdl_data.data); + + std::vector materials; + for (int i = 0; i < mdl.num_material_names; i++) { + const char* material_name = mdl.material_names[i]; + + std::string skinmtrl_path = fmt::format( + "chara/human/c{raceCode:04d}/obj/face/f{bodyCode:04d}/" + "material{}", + material_name, + fmt::arg("raceCode", physis_get_race_code(currentRace, currentSubrace, currentGender)), + fmt::arg("bodyCode", *face)); + + fmt::print("oops: {}", skinmtrl_path); + + if (physis_gamedata_exists(data, skinmtrl_path.c_str())) { + auto mat = physis_material_parse(physis_gamedata_extract_file(data, skinmtrl_path.c_str())); + materials.push_back(mat); + } + } + + mdlPart->addModel(mdl, materials, currentLod); + } + } + + if (hair) { + auto mdl_data = physis_gamedata_extract_file( + data, + physis_build_character_path( + CharacterCategory::Hair, *hair, currentRace, currentSubrace, currentGender)); + + if (mdl_data.size > 0) { + auto mdl = physis_mdl_parse(mdl_data.size, mdl_data.data); + + std::vector materials; + for (int i = 0; i < mdl.num_material_names; i++) { + const char* material_name = mdl.material_names[i]; + + std::string skinmtrl_path = fmt::format( + "chara/human/c{raceCode:04d}/obj/hair/h{bodyCode:04d}/" + "material/v0001{}", + material_name, + fmt::arg("raceCode", physis_get_race_code(currentRace, currentSubrace, currentGender)), + fmt::arg("bodyCode", *hair)); + + fmt::print("oops: {}", skinmtrl_path); + + if (physis_gamedata_exists(data, skinmtrl_path.c_str())) { + auto mat = physis_material_parse(physis_gamedata_extract_file(data, skinmtrl_path.c_str())); + materials.push_back(mat); + } + } + + mdlPart->addModel(mdl, materials, currentLod); + } + } + + if (ear) { + auto mdl_data = physis_gamedata_extract_file( + data, + physis_build_character_path( + CharacterCategory::Hair, *ear, currentRace, currentSubrace, currentGender)); + + if (mdl_data.size > 0) { + auto mdl = physis_mdl_parse(mdl_data.size, mdl_data.data); + + std::vector materials; + for (int i = 0; i < mdl.num_material_names; i++) { + const char* material_name = mdl.material_names[i]; + + std::string skinmtrl_path = fmt::format( + "chara/human/c{raceCode:04d}/obj/ear/e{bodyCode:04d}/" + "material/v0001{}", + material_name, + fmt::arg("raceCode", physis_get_race_code(currentRace, currentSubrace, currentGender)), + fmt::arg("bodyCode", *ear)); + + fmt::print("oops: {}", skinmtrl_path); + + if (physis_gamedata_exists(data, skinmtrl_path.c_str())) { + auto mat = physis_material_parse(physis_gamedata_extract_file(data, skinmtrl_path.c_str())); + materials.push_back(mat); + } + } + + mdlPart->addModel(mdl, materials, currentLod); + } + } + + if (tail) { + auto mdl_data = physis_gamedata_extract_file( + data, + physis_build_character_path( + CharacterCategory::Tail, *tail, currentRace, currentSubrace, currentGender)); + + if (mdl_data.size > 0) { + auto mdl = physis_mdl_parse(mdl_data.size, mdl_data.data); + + const char* material_name = mdl.material_names[0]; + + std::string skinmtrl_path = fmt::format( + "chara/human/c{raceCode:04d}/obj/tail/t{bodyCode:04d}/" + "material/v0001{}", + material_name, + fmt::arg("raceCode", physis_get_race_code(currentRace, currentSubrace, currentGender)), + fmt::arg("bodyCode", *tail)); + + if (physis_gamedata_exists(data, skinmtrl_path.c_str())) { + auto mat = physis_material_parse(physis_gamedata_extract_file(data, skinmtrl_path.c_str())); + mdlPart->addModel(mdl, {mat}, currentLod); + } + } + } + Q_EMIT modelReloaded(); }