diff --git a/src/core/application.cpp b/src/core/application.cpp index d5db47be..7e430d57 100644 --- a/src/core/application.cpp +++ b/src/core/application.cpp @@ -1792,16 +1792,19 @@ void Application::spawnOnlineCreature(uint64_t guid, uint32_t displayId, float x " cape=", extra.equipDisplayId[10]); // Use baked texture as-is (baked textures already include full NPC appearance) - // Equipment component textures are only for player characters with CharComponentTextureSections UV layout + // Apply to all skin-related slots: type 1 (char skin), type 2 (object skin), type 6 (hair) + // This ensures all body part batches render with the baked texture if (!extra.bakeName.empty()) { std::string bakePath = "Textures\\BakedNpcTextures\\" + extra.bakeName; GLuint finalTex = charRenderer->loadTexture(bakePath); if (finalTex != 0) { for (size_t ti = 0; ti < model.textures.size(); ti++) { - if (model.textures[ti].type == 1) { + uint32_t texType = model.textures[ti].type; + // Apply baked texture to all skin-related slots + if (texType == 1 || texType == 2 || texType == 6) { charRenderer->setModelTexture(modelId, static_cast(ti), finalTex); - LOG_DEBUG("Applied baked NPC texture to slot ", ti, ": ", bakePath); + LOG_DEBUG("Applied baked NPC texture to slot ", ti, " (type ", texType, "): ", bakePath); hasHumanoidTexture = true; } } @@ -1811,38 +1814,6 @@ void Application::spawnOnlineCreature(uint64_t guid, uint32_t displayId, float x } else { LOG_DEBUG(" Humanoid extra has empty bakeName, trying CharSections fallback"); } - - // Load hair texture from CharSections.dbc (section 3) - auto charSectionsDbc = assetManager->loadDBC("CharSections.dbc"); - if (charSectionsDbc) { - for (uint32_t r = 0; r < charSectionsDbc->getRecordCount(); r++) { - uint32_t raceId = charSectionsDbc->getUInt32(r, 1); - uint32_t sexId = charSectionsDbc->getUInt32(r, 2); - uint32_t baseSection = charSectionsDbc->getUInt32(r, 3); - uint32_t variationIndex = charSectionsDbc->getUInt32(r, 8); - uint32_t colorIndex = charSectionsDbc->getUInt32(r, 9); - - // Section 3: Hair (variation = hair style, colorIndex = hair color) - if (baseSection == 3 && - raceId == extra.raceId && sexId == extra.sexId && - variationIndex == extra.hairStyleId && colorIndex == extra.hairColorId) { - std::string hairPath = charSectionsDbc->getString(r, 4); - if (!hairPath.empty()) { - GLuint hairTex = charRenderer->loadTexture(hairPath); - if (hairTex != 0) { - // Apply to type-6 texture slot (hair) - for (size_t ti = 0; ti < model.textures.size(); ti++) { - if (model.textures[ti].type == 6) { - charRenderer->setModelTexture(modelId, static_cast(ti), hairTex); - LOG_DEBUG("Applied hair texture: ", hairPath, " to slot ", ti); - } - } - } - } - break; - } - } - } } else { LOG_WARNING(" extraDisplayId ", dispData.extraDisplayId, " not found in humanoidExtraMap"); } diff --git a/src/rendering/character_renderer.cpp b/src/rendering/character_renderer.cpp index c847a062..122bf9be 100644 --- a/src/rendering/character_renderer.cpp +++ b/src/rendering/character_renderer.cpp @@ -1217,10 +1217,21 @@ void CharacterRenderer::render(const Camera& camera, const glm::mat4& view, cons // Draw batches (submeshes) with per-batch textures // Geoset filtering: Group = submeshId / 100, Variation = submeshId % 100 - // Group 0 (body parts): render if texture is valid (skip duplicates with white/fallback texture) + // Group 0 (body parts): always render all (different body parts, not variations) // Other groups: render only if exact submeshId is in activeGeosets for (const auto& batch : gpuModel.data.batches) { - // Resolve texture for this batch first (needed for body part filtering) + if (!instance.activeGeosets.empty()) { + uint16_t group = batch.submeshId / 100; + if (group != 0) { + // Non-body groups: require exact match in activeGeosets + if (instance.activeGeosets.find(batch.submeshId) == instance.activeGeosets.end()) { + continue; + } + } + // Group 0 (body): always render + } + + // Resolve texture for this batch GLuint texId = whiteTexture; if (batch.textureIndex < gpuModel.data.textureLookup.size()) { uint16_t lookupIdx = gpuModel.data.textureLookup[batch.textureIndex]; @@ -1229,21 +1240,6 @@ void CharacterRenderer::render(const Camera& camera, const glm::mat4& view, cons } } - if (!instance.activeGeosets.empty()) { - uint16_t group = batch.submeshId / 100; - if (group == 0) { - // Body parts: skip if texture is white/fallback (duplicate mesh for compositing) - if (texId == whiteTexture) { - continue; - } - } else { - // Non-body groups: require exact match in activeGeosets - if (instance.activeGeosets.find(batch.submeshId) == instance.activeGeosets.end()) { - continue; - } - } - } - glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texId);