mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-04-17 09:33:51 +00:00
fix(rendering): filter player hair geosets via CharHairGeosets.dbc
buildDefaultPlayerGeosets() was inserting all submeshIds 0-99 into activeGeosets, showing every hair variation simultaneously. Now uses the hairGeosetMap_ (from CharHairGeosets.dbc) to select only the correct hair scalp geoset for the player's race/sex/style, matching the existing NPC geoset filtering logic in EntitySpawner.
This commit is contained in:
parent
5468a93f2e
commit
f79395788a
3 changed files with 62 additions and 11 deletions
|
|
@ -240,15 +240,63 @@ void AppearanceComposer::compositePlayerSkin(uint32_t modelSlotId, const PlayerT
|
|||
}
|
||||
}
|
||||
|
||||
std::unordered_set<uint16_t> AppearanceComposer::buildDefaultPlayerGeosets(uint8_t hairStyleId, uint8_t facialId) {
|
||||
std::unordered_set<uint16_t> AppearanceComposer::buildDefaultPlayerGeosets(uint8_t raceId, uint8_t sexId,
|
||||
uint8_t hairStyleId, uint8_t facialId) {
|
||||
std::unordered_set<uint16_t> activeGeosets;
|
||||
// Body parts (group 0: IDs 0-99, some models use up to 27)
|
||||
for (uint16_t i = 0; i <= 99; i++) activeGeosets.insert(i);
|
||||
|
||||
// Hair style geoset: group 1 = 100 + variation + 1
|
||||
activeGeosets.insert(static_cast<uint16_t>(100 + hairStyleId + 1));
|
||||
// Facial hair geoset: group 2 = 200 + variation + 1
|
||||
activeGeosets.insert(static_cast<uint16_t>(200 + facialId + 1));
|
||||
// Look up the correct hair scalp geoset from CharHairGeosets.dbc
|
||||
uint16_t selectedHairScalp = 1; // default
|
||||
std::unordered_set<uint16_t> allHairScalpGeosets;
|
||||
if (entitySpawner_) {
|
||||
const auto& hairMap = entitySpawner_->getHairGeosetMap();
|
||||
uint32_t hairKey = (static_cast<uint32_t>(raceId) << 16) |
|
||||
(static_cast<uint32_t>(sexId) << 8) |
|
||||
static_cast<uint32_t>(hairStyleId);
|
||||
auto it = hairMap.find(hairKey);
|
||||
if (it != hairMap.end() && it->second > 0)
|
||||
selectedHairScalp = it->second;
|
||||
|
||||
// Collect all hair scalp geosets for this race/sex to exclude the others
|
||||
for (const auto& [k, v] : hairMap) {
|
||||
if (static_cast<uint8_t>((k >> 16) & 0xFF) == raceId &&
|
||||
static_cast<uint8_t>((k >> 8) & 0xFF) == sexId &&
|
||||
v > 0 && v < 100)
|
||||
allHairScalpGeosets.insert(v);
|
||||
}
|
||||
}
|
||||
|
||||
// Group 0: add body base submeshes (0) but exclude other hair scalp variants
|
||||
activeGeosets.insert(0); // body base
|
||||
activeGeosets.insert(selectedHairScalp);
|
||||
// Some models have additional non-hair body submeshes (e.g. earrings, jaw);
|
||||
// these are typically < 100 and NOT in the hair geoset set
|
||||
for (uint16_t i = 1; i < 100; i++) {
|
||||
if (allHairScalpGeosets.count(i) == 0)
|
||||
activeGeosets.insert(i); // not a hair geoset, safe to include
|
||||
}
|
||||
|
||||
// Hair connector: group 1 = 100 + geoset
|
||||
activeGeosets.insert(static_cast<uint16_t>(100 + std::max<uint16_t>(selectedHairScalp, 1)));
|
||||
|
||||
// Facial hair geosets from CharacterFacialHairStyles.dbc
|
||||
if (entitySpawner_) {
|
||||
const auto& facialMap = entitySpawner_->getFacialHairGeosetMap();
|
||||
uint32_t facialKey = (static_cast<uint32_t>(raceId) << 16) |
|
||||
(static_cast<uint32_t>(sexId) << 8) |
|
||||
static_cast<uint32_t>(facialId);
|
||||
auto it = facialMap.find(facialKey);
|
||||
if (it != facialMap.end()) {
|
||||
activeGeosets.insert(static_cast<uint16_t>(200 + std::max<uint16_t>(it->second.geoset200, 1)));
|
||||
activeGeosets.insert(static_cast<uint16_t>(300 + std::max<uint16_t>(it->second.geoset300, 1)));
|
||||
} else {
|
||||
activeGeosets.insert(201);
|
||||
activeGeosets.insert(301);
|
||||
}
|
||||
} else {
|
||||
activeGeosets.insert(201);
|
||||
activeGeosets.insert(301);
|
||||
}
|
||||
|
||||
activeGeosets.insert(kGeosetBareForearms);
|
||||
activeGeosets.insert(kGeosetBareShins);
|
||||
activeGeosets.insert(kGeosetDefaultEars);
|
||||
|
|
@ -257,8 +305,6 @@ std::unordered_set<uint16_t> AppearanceComposer::buildDefaultPlayerGeosets(uint8
|
|||
activeGeosets.insert(kGeosetBarePants);
|
||||
activeGeosets.insert(kGeosetWithCape);
|
||||
activeGeosets.insert(kGeosetBareFeet);
|
||||
// 1703 = DK eye glow mesh — skip for normal characters
|
||||
// Normal eyes are part of the face texture on the body mesh
|
||||
return activeGeosets;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3707,14 +3707,18 @@ void Application::spawnPlayerCharacter() {
|
|||
// Build default geosets for the active character via AppearanceComposer
|
||||
uint8_t hairStyleId = 0;
|
||||
uint8_t facialId = 0;
|
||||
uint8_t raceId = 0;
|
||||
uint8_t sexId = 0;
|
||||
if (gameHandler) {
|
||||
if (const game::Character* ch = gameHandler->getActiveCharacter()) {
|
||||
hairStyleId = static_cast<uint8_t>((ch->appearanceBytes >> 16) & 0xFF);
|
||||
facialId = ch->facialFeatures;
|
||||
raceId = static_cast<uint8_t>(ch->race);
|
||||
sexId = static_cast<uint8_t>(ch->gender);
|
||||
}
|
||||
}
|
||||
auto activeGeosets = appearanceComposer_
|
||||
? appearanceComposer_->buildDefaultPlayerGeosets(hairStyleId, facialId)
|
||||
? appearanceComposer_->buildDefaultPlayerGeosets(raceId, sexId, hairStyleId, facialId)
|
||||
: std::unordered_set<uint16_t>{};
|
||||
charRenderer->setActiveGeosets(instanceId, activeGeosets);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue