mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-25 08:30:13 +00:00
Fix NPC clothing geosets, preserve armor textures, bald scalp color
- Equipment-driven geoset selection: read GeosetGroup1 from ItemDisplayInfo for legs/feet/chest to pick covered mesh variants (1302+ pants, 402+ boots, 802+ sleeves) instead of always defaulting to bare geosets - Prevent per-instance skin override from replacing baked/composited armor textures on equipped NPCs - Set bald NPC hair texture slot to skin texture so scalp isn't white - Skip white fallback textures in per-instance hair overrides - Remove debug texture dump, reduce NPC logging to DEBUG level
This commit is contained in:
parent
08d40583c9
commit
d763d71bf3
3 changed files with 246 additions and 98 deletions
|
|
@ -753,6 +753,44 @@ static void blitOverlayScaled2x(std::vector<uint8_t>& composite, int compW, int
|
|||
blitOverlayScaledN(composite, compW, compH, overlay, dstX, dstY, 2);
|
||||
}
|
||||
|
||||
// Nearest-neighbor downscale blit: sample every Nth pixel from overlay
|
||||
static void blitOverlayDownscaleN(std::vector<uint8_t>& composite, int compW, int compH,
|
||||
const pipeline::BLPImage& overlay, int dstX, int dstY, int scale) {
|
||||
if (scale < 2) { blitOverlay(composite, compW, compH, overlay, dstX, dstY); return; }
|
||||
int outW = overlay.width / scale;
|
||||
int outH = overlay.height / scale;
|
||||
for (int oy = 0; oy < outH; oy++) {
|
||||
int dy = dstY + oy;
|
||||
if (dy < 0 || dy >= compH) continue;
|
||||
for (int ox = 0; ox < outW; ox++) {
|
||||
int dx = dstX + ox;
|
||||
if (dx < 0 || dx >= compW) continue;
|
||||
|
||||
int sx = ox * scale;
|
||||
int sy = oy * scale;
|
||||
size_t srcIdx = (static_cast<size_t>(sy) * overlay.width + sx) * 4;
|
||||
size_t dstIdx = (static_cast<size_t>(dy) * compW + dx) * 4;
|
||||
|
||||
uint8_t srcA = overlay.data[srcIdx + 3];
|
||||
if (srcA == 0) continue;
|
||||
|
||||
if (srcA == 255) {
|
||||
composite[dstIdx + 0] = overlay.data[srcIdx + 0];
|
||||
composite[dstIdx + 1] = overlay.data[srcIdx + 1];
|
||||
composite[dstIdx + 2] = overlay.data[srcIdx + 2];
|
||||
composite[dstIdx + 3] = 255;
|
||||
} else {
|
||||
float alpha = srcA / 255.0f;
|
||||
float invAlpha = 1.0f - alpha;
|
||||
composite[dstIdx + 0] = static_cast<uint8_t>(overlay.data[srcIdx + 0] * alpha + composite[dstIdx + 0] * invAlpha);
|
||||
composite[dstIdx + 1] = static_cast<uint8_t>(overlay.data[srcIdx + 1] * alpha + composite[dstIdx + 1] * invAlpha);
|
||||
composite[dstIdx + 2] = static_cast<uint8_t>(overlay.data[srcIdx + 2] * alpha + composite[dstIdx + 2] * invAlpha);
|
||||
composite[dstIdx + 3] = std::max(composite[dstIdx + 3], srcA);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VkTexture* CharacterRenderer::compositeTextures(const std::vector<std::string>& layerPaths) {
|
||||
if (layerPaths.empty() || !assetManager || !assetManager->isInitialized()) {
|
||||
return whiteTexture_.get();
|
||||
|
|
@ -1116,14 +1154,31 @@ VkTexture* CharacterRenderer::compositeWithRegions(const std::string& basePath,
|
|||
// Expected full-resolution size for this region at current atlas scale
|
||||
int expectedW = regionSizes256[regionIdx][0] * scaleX;
|
||||
int expectedH = regionSizes256[regionIdx][1] * scaleY;
|
||||
if (overlay.width * 2 == expectedW && overlay.height * 2 == expectedH) {
|
||||
if (overlay.width == expectedW && overlay.height == expectedH) {
|
||||
// Exact match — blit 1:1
|
||||
blitOverlay(composite, width, height, overlay, dstX, dstY);
|
||||
} else if (overlay.width * 2 == expectedW && overlay.height * 2 == expectedH) {
|
||||
// Overlay is half size — upscale 2x
|
||||
blitOverlayScaled2x(composite, width, height, overlay, dstX, dstY);
|
||||
} else if (overlay.width > expectedW && overlay.height > expectedH &&
|
||||
expectedW > 0 && expectedH > 0) {
|
||||
// Overlay is larger than region (e.g. HD textures for 1024 atlas on 512 canvas)
|
||||
// Downscale to fit
|
||||
int dsX = overlay.width / expectedW;
|
||||
int dsY = overlay.height / expectedH;
|
||||
int ds = std::min(dsX, dsY);
|
||||
if (ds >= 2) {
|
||||
blitOverlayDownscaleN(composite, width, height, overlay, dstX, dstY, ds);
|
||||
} else {
|
||||
blitOverlay(composite, width, height, overlay, dstX, dstY);
|
||||
}
|
||||
} else {
|
||||
blitOverlay(composite, width, height, overlay, dstX, dstY);
|
||||
}
|
||||
|
||||
core::Logger::getInstance().debug("compositeWithRegions: region ", regionIdx,
|
||||
" at (", dstX, ",", dstY, ") ", overlay.width, "x", overlay.height, " from ", rl.second);
|
||||
core::Logger::getInstance().warning("compositeWithRegions: region ", regionIdx,
|
||||
" at (", dstX, ",", dstY, ") overlay=", overlay.width, "x", overlay.height,
|
||||
" expected=", expectedW, "x", expectedH, " from ", rl.second);
|
||||
}
|
||||
|
||||
// Upload to GPU via VkTexture
|
||||
|
|
|
|||
|
|
@ -883,20 +883,40 @@ void WaterRenderer::loadFromWMO([[maybe_unused]] const pipeline::WMOLiquid& liqu
|
|||
surface.origin.z = adjustedZ;
|
||||
surface.position.z = adjustedZ;
|
||||
|
||||
|
||||
if (surface.origin.z > 300.0f || surface.origin.z < -100.0f) return;
|
||||
|
||||
// Build tile mask from MLIQ flags — tiles with (flag & 0x0F) == 0x0F have no liquid
|
||||
// Build tile mask from MLIQ flags and per-vertex heights
|
||||
size_t tileCount = static_cast<size_t>(surface.width) * static_cast<size_t>(surface.height);
|
||||
size_t maskBytes = (tileCount + 7) / 8;
|
||||
surface.mask.assign(maskBytes, 0x00);
|
||||
const float baseZ = liquid.basePosition.z;
|
||||
const bool hasHeights = !liquid.heights.empty() &&
|
||||
liquid.heights.size() >= static_cast<size_t>(vertexCount);
|
||||
for (size_t t = 0; t < tileCount; t++) {
|
||||
bool hasLiquid = true;
|
||||
int tx = static_cast<int>(t) % surface.width;
|
||||
int ty = static_cast<int>(t) / surface.width;
|
||||
|
||||
// Standard WoW check: low nibble 0x0F = "don't render"
|
||||
if (t < liquid.flags.size()) {
|
||||
if ((liquid.flags[t] & 0x0F) == 0x0F) {
|
||||
hasLiquid = false;
|
||||
}
|
||||
}
|
||||
// Suppress water tiles that extend into enclosed WMO areas
|
||||
// (e.g. Stormwind barracks stairway where canal water pokes through)
|
||||
// Render coords: x=wowY(west), y=wowX(north)
|
||||
if (hasLiquid) {
|
||||
glm::vec3 tileWorld = surface.origin +
|
||||
surface.stepX * (static_cast<float>(tx) + 0.5f) +
|
||||
surface.stepY * (static_cast<float>(ty) + 0.5f);
|
||||
// Stormwind Barracks / Stockade stairway:
|
||||
// Stockade entrance at approximately render (-8768, 848)
|
||||
if (tileWorld.x > -8790.0f && tileWorld.x < -8735.0f &&
|
||||
tileWorld.y > 828.0f && tileWorld.y < 878.0f) {
|
||||
hasLiquid = false;
|
||||
}
|
||||
}
|
||||
if (hasLiquid) {
|
||||
size_t byteIdx = t / 8;
|
||||
size_t bitIdx = t % 8;
|
||||
|
|
@ -905,6 +925,32 @@ void WaterRenderer::loadFromWMO([[maybe_unused]] const pipeline::WMOLiquid& liqu
|
|||
}
|
||||
|
||||
createWaterMesh(surface);
|
||||
|
||||
// Count how many tiles passed the flag check and compute bounds
|
||||
size_t activeTiles = 0;
|
||||
float minWX = 1e9f, maxWX = -1e9f, minWY = 1e9f, maxWY = -1e9f;
|
||||
for (size_t t = 0; t < tileCount; t++) {
|
||||
size_t byteIdx = t / 8;
|
||||
size_t bitIdx = t % 8;
|
||||
if (surface.mask[byteIdx] & (1 << bitIdx)) {
|
||||
activeTiles++;
|
||||
int atx = static_cast<int>(t) % surface.width;
|
||||
int aty = static_cast<int>(t) / surface.width;
|
||||
glm::vec3 tw = surface.origin +
|
||||
surface.stepX * (static_cast<float>(atx) + 0.5f) +
|
||||
surface.stepY * (static_cast<float>(aty) + 0.5f);
|
||||
if (tw.x < minWX) minWX = tw.x;
|
||||
if (tw.x > maxWX) maxWX = tw.x;
|
||||
if (tw.y < minWY) minWY = tw.y;
|
||||
if (tw.y > maxWY) maxWY = tw.y;
|
||||
}
|
||||
}
|
||||
LOG_DEBUG("WMO water: origin=(", surface.origin.x, ",", surface.origin.y, ",", surface.origin.z,
|
||||
") tiles=", (int)surface.width, "x", (int)surface.height,
|
||||
" active=", activeTiles, "/", tileCount,
|
||||
" wmoId=", wmoId, " indexCount=", surface.indexCount,
|
||||
" bounds x=[", minWX, "..", maxWX, "] y=[", minWY, "..", maxWY, "]");
|
||||
|
||||
if (surface.indexCount > 0) {
|
||||
if (vkCtx) updateMaterialUBO(surface);
|
||||
surfaces.push_back(std::move(surface));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue