mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
Fix naked players and NPC gear textures
Default PLAYER_VISIBLE_ITEM layout to known WotLK 3.3.5a values (base=284, stride=2) so equipment reads work immediately without waiting for heuristic detection. Add equipment texture compositing for humanoid NPCs over baked body textures using ItemDisplayInfo.dbc region lookups (texture-only, no geoset changes to avoid invisibility).
This commit is contained in:
parent
58d8b88721
commit
d4bea91e37
3 changed files with 70 additions and 16 deletions
|
|
@ -1132,8 +1132,11 @@ private:
|
|||
|
||||
// Visible equipment for other players: detect the update-field layout (base + stride)
|
||||
// using the local player's own equipped items, then decode other players by index.
|
||||
int visibleItemEntryBase_ = -1;
|
||||
// Default to known WotLK 3.3.5a layout: UNIT_END(148) + 0x0088 = 284, stride 2.
|
||||
// The heuristic in maybeDetectVisibleItemLayout() can still override if needed.
|
||||
int visibleItemEntryBase_ = 284;
|
||||
int visibleItemStride_ = 2;
|
||||
bool visibleItemLayoutVerified_ = false; // true once heuristic confirms/overrides default
|
||||
std::unordered_map<uint64_t, std::array<uint32_t, 19>> otherPlayerVisibleItemEntries_;
|
||||
std::unordered_set<uint64_t> otherPlayerVisibleDirty_;
|
||||
std::unordered_map<uint64_t, uint32_t> otherPlayerMoveTimeMs_;
|
||||
|
|
|
|||
|
|
@ -3116,7 +3116,57 @@ void Application::spawnOnlineCreature(uint64_t guid, uint32_t displayId, float x
|
|||
// Type 6 (hair) needs its own texture from CharSections.dbc
|
||||
if (!extra.bakeName.empty()) {
|
||||
std::string bakePath = "Textures\\BakedNpcTextures\\" + extra.bakeName;
|
||||
GLuint finalTex = charRenderer->loadTexture(bakePath);
|
||||
|
||||
// Build equipment texture region layers from NPC equipment display IDs
|
||||
// (texture-only compositing — no geoset changes to avoid invisibility bugs)
|
||||
std::vector<std::pair<int, std::string>> npcRegionLayers;
|
||||
auto npcItemDisplayDbc = assetManager->loadDBC("ItemDisplayInfo.dbc");
|
||||
if (npcItemDisplayDbc) {
|
||||
static const char* npcComponentDirs[] = {
|
||||
"ArmUpperTexture", "ArmLowerTexture", "HandTexture",
|
||||
"TorsoUpperTexture", "TorsoLowerTexture",
|
||||
"LegUpperTexture", "LegLowerTexture", "FootTexture",
|
||||
};
|
||||
const bool npcIsFemale = (extra.sexId == 1);
|
||||
|
||||
// Iterate all 11 NPC equipment slots; let DBC lookup filter which have textures
|
||||
for (int eqSlot = 0; eqSlot < 11; eqSlot++) {
|
||||
uint32_t did = extra.equipDisplayId[eqSlot];
|
||||
if (did == 0) continue;
|
||||
int32_t recIdx = npcItemDisplayDbc->findRecordById(did);
|
||||
if (recIdx < 0) continue;
|
||||
|
||||
for (int region = 0; region < 8; region++) {
|
||||
std::string texName = npcItemDisplayDbc->getString(
|
||||
static_cast<uint32_t>(recIdx), 14 + region);
|
||||
if (texName.empty())
|
||||
texName = npcItemDisplayDbc->getString(
|
||||
static_cast<uint32_t>(recIdx), 15 + region);
|
||||
if (texName.empty()) continue;
|
||||
|
||||
std::string base = "Item\\TextureComponents\\" +
|
||||
std::string(npcComponentDirs[region]) + "\\" + texName;
|
||||
std::string genderPath = base + (npcIsFemale ? "_F.blp" : "_M.blp");
|
||||
std::string unisexPath = base + "_U.blp";
|
||||
std::string fullPath;
|
||||
if (assetManager->fileExists(genderPath)) fullPath = genderPath;
|
||||
else if (assetManager->fileExists(unisexPath)) fullPath = unisexPath;
|
||||
else fullPath = base + ".blp";
|
||||
|
||||
npcRegionLayers.emplace_back(region, fullPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Composite equipment textures over baked NPC texture, or just load baked texture
|
||||
GLuint finalTex = 0;
|
||||
if (!npcRegionLayers.empty()) {
|
||||
finalTex = charRenderer->compositeWithRegions(bakePath, {}, npcRegionLayers);
|
||||
LOG_DEBUG("Composited NPC baked texture with ", npcRegionLayers.size(),
|
||||
" equipment regions: ", bakePath);
|
||||
} else {
|
||||
finalTex = charRenderer->loadTexture(bakePath);
|
||||
}
|
||||
|
||||
if (finalTex != 0 && modelData) {
|
||||
for (size_t ti = 0; ti < modelData->textures.size(); ti++) {
|
||||
|
|
|
|||
|
|
@ -5681,7 +5681,7 @@ void GameHandler::rebuildOnlineInventory() {
|
|||
}
|
||||
|
||||
void GameHandler::maybeDetectVisibleItemLayout() {
|
||||
if (visibleItemEntryBase_ >= 0) return;
|
||||
if (visibleItemLayoutVerified_) return;
|
||||
if (lastPlayerFields_.empty()) return;
|
||||
|
||||
std::array<uint32_t, 19> equipEntries{};
|
||||
|
|
@ -5746,21 +5746,22 @@ void GameHandler::maybeDetectVisibleItemLayout() {
|
|||
}
|
||||
}
|
||||
|
||||
if (bestMatches < 2 || bestBase < 0 || bestStride <= 0) return;
|
||||
if (bestMismatches > 1) return;
|
||||
if (bestMatches >= 2 && bestBase >= 0 && bestStride > 0 && bestMismatches <= 1) {
|
||||
visibleItemEntryBase_ = bestBase;
|
||||
visibleItemStride_ = bestStride;
|
||||
visibleItemLayoutVerified_ = true;
|
||||
LOG_INFO("Detected PLAYER_VISIBLE_ITEM entry layout: base=", visibleItemEntryBase_,
|
||||
" stride=", visibleItemStride_, " (matches=", bestMatches,
|
||||
" mismatches=", bestMismatches, " score=", bestScore, ")");
|
||||
|
||||
visibleItemEntryBase_ = bestBase;
|
||||
visibleItemStride_ = bestStride;
|
||||
LOG_INFO("Detected PLAYER_VISIBLE_ITEM entry layout: base=", visibleItemEntryBase_,
|
||||
" stride=", visibleItemStride_, " (matches=", bestMatches,
|
||||
" mismatches=", bestMismatches, " score=", bestScore, ")");
|
||||
|
||||
// Backfill existing player entities already in view.
|
||||
for (const auto& [guid, ent] : entityManager.getEntities()) {
|
||||
if (!ent || ent->getType() != ObjectType::PLAYER) continue;
|
||||
if (guid == playerGuid) continue;
|
||||
updateOtherPlayerVisibleItems(guid, ent->getFields());
|
||||
// Backfill existing player entities already in view.
|
||||
for (const auto& [guid, ent] : entityManager.getEntities()) {
|
||||
if (!ent || ent->getType() != ObjectType::PLAYER) continue;
|
||||
if (guid == playerGuid) continue;
|
||||
updateOtherPlayerVisibleItems(guid, ent->getFields());
|
||||
}
|
||||
}
|
||||
// If heuristic didn't find a match, keep using the default WotLK layout (base=284, stride=2).
|
||||
}
|
||||
|
||||
void GameHandler::updateOtherPlayerVisibleItems(uint64_t guid, const std::map<uint16_t, uint32_t>& fields) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue