mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-03 08:03:50 +00:00
Improve equipment texture performance and accuracy
Cache composite textures by input key so identical NPC equipment combos share one GPU texture. Use DBC layout system for ItemDisplayInfo texture component fields instead of hardcoded indices (cross-expansion support). Selective player equipment re-emission on item query response instead of broadcasting to all players.
This commit is contained in:
parent
a3366931be
commit
8395f77325
8 changed files with 78 additions and 12 deletions
|
|
@ -5,7 +5,10 @@
|
||||||
},
|
},
|
||||||
"ItemDisplayInfo": {
|
"ItemDisplayInfo": {
|
||||||
"ID": 0, "LeftModel": 1, "LeftModelTexture": 3,
|
"ID": 0, "LeftModel": 1, "LeftModelTexture": 3,
|
||||||
"InventoryIcon": 5, "GeosetGroup1": 7, "GeosetGroup3": 9
|
"InventoryIcon": 5, "GeosetGroup1": 7, "GeosetGroup3": 9,
|
||||||
|
"TextureArmUpper": 14, "TextureArmLower": 15, "TextureHand": 16,
|
||||||
|
"TextureTorsoUpper": 17, "TextureTorsoLower": 18,
|
||||||
|
"TextureLegUpper": 19, "TextureLegLower": 20, "TextureFoot": 21
|
||||||
},
|
},
|
||||||
"CharSections": {
|
"CharSections": {
|
||||||
"RaceID": 1, "SexID": 2, "BaseSection": 3,
|
"RaceID": 1, "SexID": 2, "BaseSection": 3,
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,10 @@
|
||||||
},
|
},
|
||||||
"ItemDisplayInfo": {
|
"ItemDisplayInfo": {
|
||||||
"ID": 0, "LeftModel": 1, "LeftModelTexture": 3,
|
"ID": 0, "LeftModel": 1, "LeftModelTexture": 3,
|
||||||
"InventoryIcon": 5, "GeosetGroup1": 7, "GeosetGroup3": 9
|
"InventoryIcon": 5, "GeosetGroup1": 7, "GeosetGroup3": 9,
|
||||||
|
"TextureArmUpper": 14, "TextureArmLower": 15, "TextureHand": 16,
|
||||||
|
"TextureTorsoUpper": 17, "TextureTorsoLower": 18,
|
||||||
|
"TextureLegUpper": 19, "TextureLegLower": 20, "TextureFoot": 21
|
||||||
},
|
},
|
||||||
"CharSections": {
|
"CharSections": {
|
||||||
"RaceID": 1, "SexID": 2, "BaseSection": 3,
|
"RaceID": 1, "SexID": 2, "BaseSection": 3,
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,10 @@
|
||||||
},
|
},
|
||||||
"ItemDisplayInfo": {
|
"ItemDisplayInfo": {
|
||||||
"ID": 0, "LeftModel": 1, "LeftModelTexture": 3,
|
"ID": 0, "LeftModel": 1, "LeftModelTexture": 3,
|
||||||
"InventoryIcon": 5, "GeosetGroup1": 7, "GeosetGroup3": 9
|
"InventoryIcon": 5, "GeosetGroup1": 7, "GeosetGroup3": 9,
|
||||||
|
"TextureArmUpper": 14, "TextureArmLower": 15, "TextureHand": 16,
|
||||||
|
"TextureTorsoUpper": 17, "TextureTorsoLower": 18,
|
||||||
|
"TextureLegUpper": 19, "TextureLegLower": 20, "TextureFoot": 21
|
||||||
},
|
},
|
||||||
"CharSections": {
|
"CharSections": {
|
||||||
"RaceID": 1, "SexID": 2, "BaseSection": 3,
|
"RaceID": 1, "SexID": 2, "BaseSection": 3,
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,10 @@
|
||||||
},
|
},
|
||||||
"ItemDisplayInfo": {
|
"ItemDisplayInfo": {
|
||||||
"ID": 0, "LeftModel": 1, "LeftModelTexture": 3,
|
"ID": 0, "LeftModel": 1, "LeftModelTexture": 3,
|
||||||
"InventoryIcon": 5, "GeosetGroup1": 7, "GeosetGroup3": 9
|
"InventoryIcon": 5, "GeosetGroup1": 7, "GeosetGroup3": 9,
|
||||||
|
"TextureArmUpper": 14, "TextureArmLower": 15, "TextureHand": 16,
|
||||||
|
"TextureTorsoUpper": 17, "TextureTorsoLower": 18,
|
||||||
|
"TextureLegUpper": 19, "TextureLegLower": 20, "TextureFoot": 21
|
||||||
},
|
},
|
||||||
"CharSections": {
|
"CharSections": {
|
||||||
"RaceID": 1, "SexID": 2, "BaseSection": 3,
|
"RaceID": 1, "SexID": 2, "BaseSection": 3,
|
||||||
|
|
|
||||||
|
|
@ -247,6 +247,7 @@ private:
|
||||||
uint64_t lastUse = 0;
|
uint64_t lastUse = 0;
|
||||||
};
|
};
|
||||||
std::unordered_map<std::string, TextureCacheEntry> textureCache;
|
std::unordered_map<std::string, TextureCacheEntry> textureCache;
|
||||||
|
std::unordered_map<std::string, GLuint> compositeCache_; // key → GPU texture for reuse
|
||||||
size_t textureCacheBytes_ = 0;
|
size_t textureCacheBytes_ = 0;
|
||||||
uint64_t textureCacheCounter_ = 0;
|
uint64_t textureCacheCounter_ = 0;
|
||||||
size_t textureCacheBudgetBytes_ = 1024ull * 1024 * 1024; // Default, overridden at init
|
size_t textureCacheBudgetBytes_ = 1024ull * 1024 * 1024; // Default, overridden at init
|
||||||
|
|
|
||||||
|
|
@ -3127,6 +3127,19 @@ void Application::spawnOnlineCreature(uint64_t guid, uint32_t displayId, float x
|
||||||
"TorsoUpperTexture", "TorsoLowerTexture",
|
"TorsoUpperTexture", "TorsoLowerTexture",
|
||||||
"LegUpperTexture", "LegLowerTexture", "FootTexture",
|
"LegUpperTexture", "LegLowerTexture", "FootTexture",
|
||||||
};
|
};
|
||||||
|
const auto* idiL = pipeline::getActiveDBCLayout()
|
||||||
|
? pipeline::getActiveDBCLayout()->getLayout("ItemDisplayInfo") : nullptr;
|
||||||
|
// Texture component region fields (8 regions: ArmUpper..Foot)
|
||||||
|
const uint32_t texRegionFields[8] = {
|
||||||
|
idiL ? (*idiL)["TextureArmUpper"] : 14u,
|
||||||
|
idiL ? (*idiL)["TextureArmLower"] : 15u,
|
||||||
|
idiL ? (*idiL)["TextureHand"] : 16u,
|
||||||
|
idiL ? (*idiL)["TextureTorsoUpper"]: 17u,
|
||||||
|
idiL ? (*idiL)["TextureTorsoLower"]: 18u,
|
||||||
|
idiL ? (*idiL)["TextureLegUpper"] : 19u,
|
||||||
|
idiL ? (*idiL)["TextureLegLower"] : 20u,
|
||||||
|
idiL ? (*idiL)["TextureFoot"] : 21u,
|
||||||
|
};
|
||||||
const bool npcIsFemale = (extra.sexId == 1);
|
const bool npcIsFemale = (extra.sexId == 1);
|
||||||
|
|
||||||
// Iterate all 11 NPC equipment slots; let DBC lookup filter which have textures
|
// Iterate all 11 NPC equipment slots; let DBC lookup filter which have textures
|
||||||
|
|
@ -3138,10 +3151,7 @@ void Application::spawnOnlineCreature(uint64_t guid, uint32_t displayId, float x
|
||||||
|
|
||||||
for (int region = 0; region < 8; region++) {
|
for (int region = 0; region < 8; region++) {
|
||||||
std::string texName = npcItemDisplayDbc->getString(
|
std::string texName = npcItemDisplayDbc->getString(
|
||||||
static_cast<uint32_t>(recIdx), 14 + region);
|
static_cast<uint32_t>(recIdx), texRegionFields[region]);
|
||||||
if (texName.empty())
|
|
||||||
texName = npcItemDisplayDbc->getString(
|
|
||||||
static_cast<uint32_t>(recIdx), 15 + region);
|
|
||||||
if (texName.empty()) continue;
|
if (texName.empty()) continue;
|
||||||
|
|
||||||
std::string base = "Item\\TextureComponents\\" +
|
std::string base = "Item\\TextureComponents\\" +
|
||||||
|
|
@ -3958,6 +3968,18 @@ void Application::setOnlinePlayerEquipment(uint64_t guid,
|
||||||
"FootTexture",
|
"FootTexture",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Texture component region fields from DBC layout
|
||||||
|
const uint32_t texRegionFields[8] = {
|
||||||
|
idiL ? (*idiL)["TextureArmUpper"] : 14u,
|
||||||
|
idiL ? (*idiL)["TextureArmLower"] : 15u,
|
||||||
|
idiL ? (*idiL)["TextureHand"] : 16u,
|
||||||
|
idiL ? (*idiL)["TextureTorsoUpper"]: 17u,
|
||||||
|
idiL ? (*idiL)["TextureTorsoLower"]: 18u,
|
||||||
|
idiL ? (*idiL)["TextureLegUpper"] : 19u,
|
||||||
|
idiL ? (*idiL)["TextureLegLower"] : 20u,
|
||||||
|
idiL ? (*idiL)["TextureFoot"] : 21u,
|
||||||
|
};
|
||||||
|
|
||||||
std::vector<std::pair<int, std::string>> regionLayers;
|
std::vector<std::pair<int, std::string>> regionLayers;
|
||||||
const bool isFemale = (st.genderId == 1);
|
const bool isFemale = (st.genderId == 1);
|
||||||
|
|
||||||
|
|
@ -3968,8 +3990,8 @@ void Application::setOnlinePlayerEquipment(uint64_t guid,
|
||||||
if (recIdx < 0) continue;
|
if (recIdx < 0) continue;
|
||||||
|
|
||||||
for (int region = 0; region < 8; region++) {
|
for (int region = 0; region < 8; region++) {
|
||||||
std::string texName = displayInfoDbc->getString(static_cast<uint32_t>(recIdx), 14 + region);
|
std::string texName = displayInfoDbc->getString(
|
||||||
if (texName.empty()) texName = displayInfoDbc->getString(static_cast<uint32_t>(recIdx), 15 + region);
|
static_cast<uint32_t>(recIdx), texRegionFields[region]);
|
||||||
if (texName.empty()) continue;
|
if (texName.empty()) continue;
|
||||||
|
|
||||||
std::string base = "Item\\TextureComponents\\" + std::string(componentDirs[region]) + "\\" + texName;
|
std::string base = "Item\\TextureComponents\\" + std::string(componentDirs[region]) + "\\" + texName;
|
||||||
|
|
|
||||||
|
|
@ -5251,10 +5251,25 @@ void GameHandler::handleItemQueryResponse(network::Packet& packet) {
|
||||||
itemInfoCache_[data.entry] = data;
|
itemInfoCache_[data.entry] = data;
|
||||||
rebuildOnlineInventory();
|
rebuildOnlineInventory();
|
||||||
maybeDetectVisibleItemLayout();
|
maybeDetectVisibleItemLayout();
|
||||||
emitAllOtherPlayerEquipment();
|
|
||||||
// If we have inspect-based item entry lists, re-emit for any players that now resolve.
|
// Selectively re-emit only players whose equipment references this item entry
|
||||||
|
const uint32_t resolvedEntry = data.entry;
|
||||||
|
for (const auto& [guid, entries] : otherPlayerVisibleItemEntries_) {
|
||||||
|
for (uint32_t e : entries) {
|
||||||
|
if (e == resolvedEntry) {
|
||||||
|
emitOtherPlayerEquipment(guid);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Same for inspect-based entries
|
||||||
if (playerEquipmentCallback_) {
|
if (playerEquipmentCallback_) {
|
||||||
for (const auto& [guid, entries] : inspectedPlayerItemEntries_) {
|
for (const auto& [guid, entries] : inspectedPlayerItemEntries_) {
|
||||||
|
bool relevant = false;
|
||||||
|
for (uint32_t e : entries) {
|
||||||
|
if (e == resolvedEntry) { relevant = true; break; }
|
||||||
|
}
|
||||||
|
if (!relevant) continue;
|
||||||
std::array<uint32_t, 19> displayIds{};
|
std::array<uint32_t, 19> displayIds{};
|
||||||
std::array<uint8_t, 19> invTypes{};
|
std::array<uint8_t, 19> invTypes{};
|
||||||
for (int s = 0; s < 19; s++) {
|
for (int s = 0; s < 19; s++) {
|
||||||
|
|
|
||||||
|
|
@ -572,6 +572,21 @@ GLuint CharacterRenderer::compositeTextures(const std::vector<std::string>& laye
|
||||||
GLuint CharacterRenderer::compositeWithRegions(const std::string& basePath,
|
GLuint CharacterRenderer::compositeWithRegions(const std::string& basePath,
|
||||||
const std::vector<std::string>& baseLayers,
|
const std::vector<std::string>& baseLayers,
|
||||||
const std::vector<std::pair<int, std::string>>& regionLayers) {
|
const std::vector<std::pair<int, std::string>>& regionLayers) {
|
||||||
|
// Build cache key from all inputs to avoid redundant compositing
|
||||||
|
std::string cacheKey = basePath;
|
||||||
|
for (const auto& bl : baseLayers) { cacheKey += '|'; cacheKey += bl; }
|
||||||
|
cacheKey += '#';
|
||||||
|
for (const auto& rl : regionLayers) {
|
||||||
|
cacheKey += std::to_string(rl.first);
|
||||||
|
cacheKey += ':';
|
||||||
|
cacheKey += rl.second;
|
||||||
|
cacheKey += ',';
|
||||||
|
}
|
||||||
|
auto cacheIt = compositeCache_.find(cacheKey);
|
||||||
|
if (cacheIt != compositeCache_.end() && cacheIt->second != 0) {
|
||||||
|
return cacheIt->second;
|
||||||
|
}
|
||||||
|
|
||||||
// Region index → pixel coordinates on the 512x512 atlas
|
// Region index → pixel coordinates on the 512x512 atlas
|
||||||
static const int regionCoords[][2] = {
|
static const int regionCoords[][2] = {
|
||||||
{ 0, 0 }, // 0 = ArmUpper
|
{ 0, 0 }, // 0 = ArmUpper
|
||||||
|
|
@ -717,6 +732,7 @@ GLuint CharacterRenderer::compositeWithRegions(const std::string& basePath,
|
||||||
|
|
||||||
core::Logger::getInstance().info("compositeWithRegions: created ", width, "x", height,
|
core::Logger::getInstance().info("compositeWithRegions: created ", width, "x", height,
|
||||||
" texture with ", regionLayers.size(), " equipment regions");
|
" texture with ", regionLayers.size(), " equipment regions");
|
||||||
|
compositeCache_[cacheKey] = texId;
|
||||||
return texId;
|
return texId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue