From 10ceb2b3a85849d6844dc092a7df1196981e93e2 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Thu, 5 Feb 2026 23:05:35 -0800 Subject: [PATCH] Add helmet model attachment and fix NPC textures Load and attach helmet M2 models to humanoid NPCs from ItemDisplayInfo.dbc. Revert equipment texture compositing since baked NPC textures are 256x256 (incompatible with 512x512 region system). Add debug logging for equipment display IDs. --- src/core/application.cpp | 60 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/src/core/application.cpp b/src/core/application.cpp index c11d9e04..99d47822 100644 --- a/src/core/application.cpp +++ b/src/core/application.cpp @@ -1783,7 +1783,15 @@ void Application::spawnOnlineCreature(uint64_t guid, uint32_t displayId, float x LOG_DEBUG(" Found humanoid extra: raceId=", (int)extra.raceId, " sexId=", (int)extra.sexId, " hairStyle=", (int)extra.hairStyleId, " hairColor=", (int)extra.hairColorId, " bakeName='", extra.bakeName, "'"); - // Use baked texture if available (bakeName already includes .blp extension) + LOG_DEBUG(" Equipment: helm=", extra.equipDisplayId[0], " shoulder=", extra.equipDisplayId[1], + " shirt=", extra.equipDisplayId[2], " chest=", extra.equipDisplayId[3], + " belt=", extra.equipDisplayId[4], " legs=", extra.equipDisplayId[5], + " feet=", extra.equipDisplayId[6], " wrist=", extra.equipDisplayId[7], + " hands=", extra.equipDisplayId[8], " tabard=", extra.equipDisplayId[9], + " cape=", extra.equipDisplayId[10]); + + // Use baked texture directly (256x256 - equipment overlays not compatible) + // Baked NPC textures already include the complete body skin with face if (!extra.bakeName.empty()) { std::string bakePath = "Textures\\BakedNpcTextures\\" + extra.bakeName; GLuint bakeTex = charRenderer->loadTexture(bakePath); @@ -1992,6 +2000,56 @@ void Application::spawnOnlineCreature(uint64_t guid, uint32_t displayId, float x " facial=", 201 + extra.facialHairId, " chest=", geosetChest, " pants=", geosetPants, " boots=", geosetBoots, " gloves=", geosetGloves); + + // Load and attach helmet model if equipped + if (extra.equipDisplayId[0] != 0 && itemDisplayDbc) { + int32_t helmIdx = itemDisplayDbc->findRecordById(extra.equipDisplayId[0]); + if (helmIdx >= 0) { + // Get helmet model name from ItemDisplayInfo.dbc (col 1 = LeftModel) + std::string helmModelName = itemDisplayDbc->getString(static_cast(helmIdx), 1); + if (!helmModelName.empty()) { + // Convert .mdx to .m2 + size_t dotPos = helmModelName.rfind('.'); + if (dotPos != std::string::npos) { + helmModelName = helmModelName.substr(0, dotPos) + ".m2"; + } else { + helmModelName += ".m2"; + } + + // Try to load helmet from various paths + std::string helmPath = "Item\\ObjectComponents\\Head\\" + helmModelName; + auto helmData = assetManager->readFile(helmPath); + if (helmData.empty()) { + // Try alternate path + helmPath = "Item\\ObjectComponents\\Helmet\\" + helmModelName; + helmData = assetManager->readFile(helmPath); + } + + if (!helmData.empty()) { + auto helmModel = pipeline::M2Loader::load(helmData); + // Load skin + std::string skinPath = helmPath.substr(0, helmPath.size() - 3) + "00.skin"; + auto skinData = assetManager->readFile(skinPath); + if (!skinData.empty()) { + pipeline::M2Loader::loadSkin(skinData, helmModel); + } + + if (helmModel.isValid()) { + // Attachment point 11 = Head + uint32_t helmModelId = nextCreatureModelId_++; + // Get texture from ItemDisplayInfo (col 3 = LeftModelTexture) + std::string helmTexName = itemDisplayDbc->getString(static_cast(helmIdx), 3); + std::string helmTexPath; + if (!helmTexName.empty()) { + helmTexPath = "Item\\ObjectComponents\\Head\\" + helmTexName + ".blp"; + } + charRenderer->attachWeapon(instanceId, 11, helmModel, helmModelId, helmTexPath); + LOG_DEBUG("Attached helmet model: ", helmPath); + } + } + } + } + } } }