diff --git a/include/rendering/character_renderer.hpp b/include/rendering/character_renderer.hpp index 69b8f5df..c368ded4 100644 --- a/include/rendering/character_renderer.hpp +++ b/include/rendering/character_renderer.hpp @@ -293,8 +293,11 @@ private: bool normalMapPending = false; // deferred normal map generation }; std::unordered_map textureCache; - std::unordered_map textureHasAlphaByPtr_; - std::unordered_map textureColorKeyBlackByPtr_; + struct TextureProperties { + bool hasAlpha = false; + bool colorKeyBlack = false; + }; + std::unordered_map texturePropsByPtr_; std::unordered_map compositeCache_; // key → texture for reuse std::unordered_set failedTextureCache_; // negative cache for budget exhaustion std::unordered_map failedTextureRetryAt_; diff --git a/include/rendering/m2_renderer.hpp b/include/rendering/m2_renderer.hpp index fe8d7f61..ac99c5df 100644 --- a/include/rendering/m2_renderer.hpp +++ b/include/rendering/m2_renderer.hpp @@ -483,8 +483,11 @@ private: bool colorKeyBlack = false; }; std::unordered_map textureCache; - std::unordered_map textureHasAlphaByPtr_; - std::unordered_map textureColorKeyBlackByPtr_; + struct TextureProperties { + bool hasAlpha = false; + bool colorKeyBlack = false; + }; + std::unordered_map texturePropsByPtr_; size_t textureCacheBytes_ = 0; uint64_t textureCacheCounter_ = 0; size_t textureCacheBudgetBytes_ = 2048ull * 1024 * 1024; diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index 17b6a98e..1d7d80a3 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -21962,12 +21962,21 @@ void GameHandler::loadSpellNameCache() const { if (f != 0xFFFFFFFF && f < dbc->getFieldCount()) tooltipField = f; } + // Cache field indices before the loop to avoid repeated layout lookups + const uint32_t idField = spellL ? (*spellL)["ID"] : 0; + const uint32_t nameField = spellL ? (*spellL)["Name"] : 136; + const uint32_t rankField = spellL ? (*spellL)["Rank"] : 153; + const uint32_t ebp0Field = spellL ? spellL->field("EffectBasePoints0") : 0xFFFFFFFF; + const uint32_t ebp1Field = spellL ? spellL->field("EffectBasePoints1") : 0xFFFFFFFF; + const uint32_t ebp2Field = spellL ? spellL->field("EffectBasePoints2") : 0xFFFFFFFF; + const uint32_t durIdxField = spellL ? spellL->field("DurationIndex") : 0xFFFFFFFF; + uint32_t count = dbc->getRecordCount(); for (uint32_t i = 0; i < count; ++i) { - uint32_t id = dbc->getUInt32(i, spellL ? (*spellL)["ID"] : 0); + uint32_t id = dbc->getUInt32(i, idField); if (id == 0) continue; - std::string name = dbc->getString(i, spellL ? (*spellL)["Name"] : 136); - std::string rank = dbc->getString(i, spellL ? (*spellL)["Rank"] : 153); + std::string name = dbc->getString(i, nameField); + std::string rank = dbc->getString(i, rankField); if (!name.empty()) { SpellNameEntry entry{std::move(name), std::move(rank), {}, 0, 0, 0}; if (tooltipField != 0xFFFFFFFF) { @@ -21988,20 +21997,12 @@ void GameHandler::loadSpellNameCache() const { entry.attrEx = dbc->getUInt32(i, attrExField); } // Load effect base points for $s1/$s2/$s3 tooltip substitution - if (spellL) { - uint32_t f0 = spellL->field("EffectBasePoints0"); - uint32_t f1 = spellL->field("EffectBasePoints1"); - uint32_t f2 = spellL->field("EffectBasePoints2"); - if (f0 != 0xFFFFFFFF) entry.effectBasePoints[0] = static_cast(dbc->getUInt32(i, f0)); - if (f1 != 0xFFFFFFFF) entry.effectBasePoints[1] = static_cast(dbc->getUInt32(i, f1)); - if (f2 != 0xFFFFFFFF) entry.effectBasePoints[2] = static_cast(dbc->getUInt32(i, f2)); - } + if (ebp0Field != 0xFFFFFFFF) entry.effectBasePoints[0] = static_cast(dbc->getUInt32(i, ebp0Field)); + if (ebp1Field != 0xFFFFFFFF) entry.effectBasePoints[1] = static_cast(dbc->getUInt32(i, ebp1Field)); + if (ebp2Field != 0xFFFFFFFF) entry.effectBasePoints[2] = static_cast(dbc->getUInt32(i, ebp2Field)); // Duration: read DurationIndex and resolve via SpellDuration.dbc later - if (spellL) { - uint32_t durF = spellL->field("DurationIndex"); - if (durF != 0xFFFFFFFF) - entry.durationSec = static_cast(dbc->getUInt32(i, durF)); // store index temporarily - } + if (durIdxField != 0xFFFFFFFF) + entry.durationSec = static_cast(dbc->getUInt32(i, durIdxField)); // store index temporarily spellNameCache_[id] = std::move(entry); } } @@ -22036,9 +22037,11 @@ void GameHandler::loadSkillLineAbilityDbc() { auto slaDbc = am->loadDBC("SkillLineAbility.dbc"); if (slaDbc && slaDbc->isLoaded()) { const auto* slaL = pipeline::getActiveDBCLayout() ? pipeline::getActiveDBCLayout()->getLayout("SkillLineAbility") : nullptr; + const uint32_t slaSkillField = slaL ? (*slaL)["SkillLineID"] : 1; + const uint32_t slaSpellField = slaL ? (*slaL)["SpellID"] : 2; for (uint32_t i = 0; i < slaDbc->getRecordCount(); i++) { - uint32_t skillLineId = slaDbc->getUInt32(i, slaL ? (*slaL)["SkillLineID"] : 1); - uint32_t spellId = slaDbc->getUInt32(i, slaL ? (*slaL)["SpellID"] : 2); + uint32_t skillLineId = slaDbc->getUInt32(i, slaSkillField); + uint32_t spellId = slaDbc->getUInt32(i, slaSpellField); if (spellId > 0 && skillLineId > 0) { spellToSkillLine_[spellId] = skillLineId; } @@ -22233,16 +22236,23 @@ void GameHandler::loadTalentDbc() { // 23-39: BackgroundFile (16 localized strings + flags = 17 fields) const auto* ttL = pipeline::getActiveDBCLayout() ? pipeline::getActiveDBCLayout()->getLayout("TalentTab") : nullptr; + // Cache field indices before the loop + const uint32_t ttIdField = ttL ? (*ttL)["ID"] : 0; + const uint32_t ttNameField = ttL ? (*ttL)["Name"] : 1; + const uint32_t ttClassField = ttL ? (*ttL)["ClassMask"] : 20; + const uint32_t ttOrderField = ttL ? (*ttL)["OrderIndex"] : 22; + const uint32_t ttBgField = ttL ? (*ttL)["BackgroundFile"] : 23; + uint32_t count = tabDbc->getRecordCount(); for (uint32_t i = 0; i < count; ++i) { TalentTabEntry entry; - entry.tabId = tabDbc->getUInt32(i, ttL ? (*ttL)["ID"] : 0); + entry.tabId = tabDbc->getUInt32(i, ttIdField); if (entry.tabId == 0) continue; - entry.name = tabDbc->getString(i, ttL ? (*ttL)["Name"] : 1); - entry.classMask = tabDbc->getUInt32(i, ttL ? (*ttL)["ClassMask"] : 20); - entry.orderIndex = static_cast(tabDbc->getUInt32(i, ttL ? (*ttL)["OrderIndex"] : 22)); - entry.backgroundFile = tabDbc->getString(i, ttL ? (*ttL)["BackgroundFile"] : 23); + entry.name = tabDbc->getString(i, ttNameField); + entry.classMask = tabDbc->getUInt32(i, ttClassField); + entry.orderIndex = static_cast(tabDbc->getUInt32(i, ttOrderField)); + entry.backgroundFile = tabDbc->getString(i, ttBgField); talentTabCache_[entry.tabId] = entry; @@ -22694,19 +22704,26 @@ void GameHandler::loadTaxiDbc() { auto nodesDbc = am->loadDBC("TaxiNodes.dbc"); if (nodesDbc && nodesDbc->isLoaded()) { const auto* tnL = pipeline::getActiveDBCLayout() ? pipeline::getActiveDBCLayout()->getLayout("TaxiNodes") : nullptr; + // Cache field indices before the loop + const uint32_t tnIdField = tnL ? (*tnL)["ID"] : 0; + const uint32_t tnMapField = tnL ? (*tnL)["MapID"] : 1; + const uint32_t tnXField = tnL ? (*tnL)["X"] : 2; + const uint32_t tnYField = tnL ? (*tnL)["Y"] : 3; + const uint32_t tnZField = tnL ? (*tnL)["Z"] : 4; + const uint32_t tnNameField = tnL ? (*tnL)["Name"] : 5; + const uint32_t mountAllianceField = tnL ? (*tnL)["MountDisplayIdAlliance"] : 22; + const uint32_t mountHordeField = tnL ? (*tnL)["MountDisplayIdHorde"] : 23; + const uint32_t mountAllianceFB = tnL ? (*tnL)["MountDisplayIdAllianceFallback"] : 20; + const uint32_t mountHordeFB = tnL ? (*tnL)["MountDisplayIdHordeFallback"] : 21; uint32_t fieldCount = nodesDbc->getFieldCount(); for (uint32_t i = 0; i < nodesDbc->getRecordCount(); i++) { TaxiNode node; - node.id = nodesDbc->getUInt32(i, tnL ? (*tnL)["ID"] : 0); - node.mapId = nodesDbc->getUInt32(i, tnL ? (*tnL)["MapID"] : 1); - node.x = nodesDbc->getFloat(i, tnL ? (*tnL)["X"] : 2); - node.y = nodesDbc->getFloat(i, tnL ? (*tnL)["Y"] : 3); - node.z = nodesDbc->getFloat(i, tnL ? (*tnL)["Z"] : 4); - node.name = nodesDbc->getString(i, tnL ? (*tnL)["Name"] : 5); - const uint32_t mountAllianceField = tnL ? (*tnL)["MountDisplayIdAlliance"] : 22; - const uint32_t mountHordeField = tnL ? (*tnL)["MountDisplayIdHorde"] : 23; - const uint32_t mountAllianceFB = tnL ? (*tnL)["MountDisplayIdAllianceFallback"] : 20; - const uint32_t mountHordeFB = tnL ? (*tnL)["MountDisplayIdHordeFallback"] : 21; + node.id = nodesDbc->getUInt32(i, tnIdField); + node.mapId = nodesDbc->getUInt32(i, tnMapField); + node.x = nodesDbc->getFloat(i, tnXField); + node.y = nodesDbc->getFloat(i, tnYField); + node.z = nodesDbc->getFloat(i, tnZField); + node.name = nodesDbc->getString(i, tnNameField); if (fieldCount > mountHordeField) { node.mountDisplayIdAlliance = nodesDbc->getUInt32(i, mountAllianceField); node.mountDisplayIdHorde = nodesDbc->getUInt32(i, mountHordeField); @@ -22735,12 +22752,16 @@ void GameHandler::loadTaxiDbc() { auto pathDbc = am->loadDBC("TaxiPath.dbc"); if (pathDbc && pathDbc->isLoaded()) { const auto* tpL = pipeline::getActiveDBCLayout() ? pipeline::getActiveDBCLayout()->getLayout("TaxiPath") : nullptr; + const uint32_t tpIdField = tpL ? (*tpL)["ID"] : 0; + const uint32_t tpFromField = tpL ? (*tpL)["FromNode"] : 1; + const uint32_t tpToField = tpL ? (*tpL)["ToNode"] : 2; + const uint32_t tpCostField = tpL ? (*tpL)["Cost"] : 3; for (uint32_t i = 0; i < pathDbc->getRecordCount(); i++) { TaxiPathEdge edge; - edge.pathId = pathDbc->getUInt32(i, tpL ? (*tpL)["ID"] : 0); - edge.fromNode = pathDbc->getUInt32(i, tpL ? (*tpL)["FromNode"] : 1); - edge.toNode = pathDbc->getUInt32(i, tpL ? (*tpL)["ToNode"] : 2); - edge.cost = pathDbc->getUInt32(i, tpL ? (*tpL)["Cost"] : 3); + edge.pathId = pathDbc->getUInt32(i, tpIdField); + edge.fromNode = pathDbc->getUInt32(i, tpFromField); + edge.toNode = pathDbc->getUInt32(i, tpToField); + edge.cost = pathDbc->getUInt32(i, tpCostField); taxiPathEdges_.push_back(edge); } LOG_INFO("Loaded ", taxiPathEdges_.size(), " taxi path edges from TaxiPath.dbc"); @@ -22751,15 +22772,22 @@ void GameHandler::loadTaxiDbc() { auto pathNodeDbc = am->loadDBC("TaxiPathNode.dbc"); if (pathNodeDbc && pathNodeDbc->isLoaded()) { const auto* tpnL = pipeline::getActiveDBCLayout() ? pipeline::getActiveDBCLayout()->getLayout("TaxiPathNode") : nullptr; + const uint32_t tpnIdField = tpnL ? (*tpnL)["ID"] : 0; + const uint32_t tpnPathField = tpnL ? (*tpnL)["PathID"] : 1; + const uint32_t tpnIndexField = tpnL ? (*tpnL)["NodeIndex"] : 2; + const uint32_t tpnMapField = tpnL ? (*tpnL)["MapID"] : 3; + const uint32_t tpnXField = tpnL ? (*tpnL)["X"] : 4; + const uint32_t tpnYField = tpnL ? (*tpnL)["Y"] : 5; + const uint32_t tpnZField = tpnL ? (*tpnL)["Z"] : 6; for (uint32_t i = 0; i < pathNodeDbc->getRecordCount(); i++) { TaxiPathNode node; - node.id = pathNodeDbc->getUInt32(i, tpnL ? (*tpnL)["ID"] : 0); - node.pathId = pathNodeDbc->getUInt32(i, tpnL ? (*tpnL)["PathID"] : 1); - node.nodeIndex = pathNodeDbc->getUInt32(i, tpnL ? (*tpnL)["NodeIndex"] : 2); - node.mapId = pathNodeDbc->getUInt32(i, tpnL ? (*tpnL)["MapID"] : 3); - node.x = pathNodeDbc->getFloat(i, tpnL ? (*tpnL)["X"] : 4); - node.y = pathNodeDbc->getFloat(i, tpnL ? (*tpnL)["Y"] : 5); - node.z = pathNodeDbc->getFloat(i, tpnL ? (*tpnL)["Z"] : 6); + node.id = pathNodeDbc->getUInt32(i, tpnIdField); + node.pathId = pathNodeDbc->getUInt32(i, tpnPathField); + node.nodeIndex = pathNodeDbc->getUInt32(i, tpnIndexField); + node.mapId = pathNodeDbc->getUInt32(i, tpnMapField); + node.x = pathNodeDbc->getFloat(i, tpnXField); + node.y = pathNodeDbc->getFloat(i, tpnYField); + node.z = pathNodeDbc->getFloat(i, tpnZField); taxiPathNodes_[node.pathId].push_back(node); } // Sort waypoints by nodeIndex for each path @@ -23857,10 +23885,13 @@ void GameHandler::loadSkillLineDbc() { } const auto* slL = pipeline::getActiveDBCLayout() ? pipeline::getActiveDBCLayout()->getLayout("SkillLine") : nullptr; + const uint32_t slIdField = slL ? (*slL)["ID"] : 0; + const uint32_t slCatField = slL ? (*slL)["Category"] : 1; + const uint32_t slNameField = slL ? (*slL)["Name"] : 3; for (uint32_t i = 0; i < dbc->getRecordCount(); i++) { - uint32_t id = dbc->getUInt32(i, slL ? (*slL)["ID"] : 0); - uint32_t category = dbc->getUInt32(i, slL ? (*slL)["Category"] : 1); - std::string name = dbc->getString(i, slL ? (*slL)["Name"] : 3); + uint32_t id = dbc->getUInt32(i, slIdField); + uint32_t category = dbc->getUInt32(i, slCatField); + std::string name = dbc->getString(i, slNameField); if (id > 0 && !name.empty()) { skillLineNames_[id] = name; skillLineCategories_[id] = category; diff --git a/src/rendering/camera_controller.cpp b/src/rendering/camera_controller.cpp index feec51f7..2da20ebd 100644 --- a/src/rendering/camera_controller.cpp +++ b/src/rendering/camera_controller.cpp @@ -18,6 +18,8 @@ namespace rendering { namespace { +constexpr float kMaxPhysicsDelta = 1.0f / 30.0f; + std::optional selectReachableFloor(const std::optional& terrainH, const std::optional& wmoH, float refZ, @@ -157,7 +159,7 @@ void CameraController::update(float deltaTime) { return; } // Keep physics integration stable during render hitches to avoid floor tunneling. - const float physicsDeltaTime = std::min(deltaTime, 1.0f / 30.0f); + const float physicsDeltaTime = std::min(deltaTime, kMaxPhysicsDelta); // During taxi flights, skip movement logic but keep camera orbit/zoom controls. if (externalFollow_) { diff --git a/src/rendering/character_renderer.cpp b/src/rendering/character_renderer.cpp index f9e5352a..6432c672 100644 --- a/src/rendering/character_renderer.cpp +++ b/src/rendering/character_renderer.cpp @@ -342,8 +342,7 @@ void CharacterRenderer::shutdown() { // Clean up texture cache (VkTexture unique_ptrs auto-destroy) textureCache.clear(); - textureHasAlphaByPtr_.clear(); - textureColorKeyBlackByPtr_.clear(); + texturePropsByPtr_.clear(); textureCacheBytes_ = 0; textureCacheCounter_ = 0; @@ -437,8 +436,7 @@ void CharacterRenderer::clear() { // Clear texture cache (VkTexture unique_ptrs auto-destroy) textureCache.clear(); - textureHasAlphaByPtr_.clear(); - textureColorKeyBlackByPtr_.clear(); + texturePropsByPtr_.clear(); textureCacheBytes_ = 0; textureCacheCounter_ = 0; loggedTextureLoadFails_.clear(); @@ -745,8 +743,7 @@ VkTexture* CharacterRenderer::loadTexture(const std::string& path) { } textureCacheBytes_ += e.approxBytes; - textureHasAlphaByPtr_[texPtr] = hasAlpha; - textureColorKeyBlackByPtr_[texPtr] = colorKeyBlackHint; + texturePropsByPtr_[texPtr] = {hasAlpha, colorKeyBlackHint}; textureCache[key] = std::move(e); failedTextureCache_.erase(key); failedTextureRetryAt_.erase(key); @@ -2297,10 +2294,11 @@ void CharacterRenderer::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, bool alphaCutout = false; bool colorKeyBlack = false; if (texPtr != nullptr && texPtr != whiteTexture_.get()) { - auto ait = textureHasAlphaByPtr_.find(texPtr); - alphaCutout = (ait != textureHasAlphaByPtr_.end()) ? ait->second : false; - auto cit = textureColorKeyBlackByPtr_.find(texPtr); - colorKeyBlack = (cit != textureColorKeyBlackByPtr_.end()) ? cit->second : false; + auto pit = texturePropsByPtr_.find(texPtr); + if (pit != texturePropsByPtr_.end()) { + alphaCutout = pit->second.hasAlpha; + colorKeyBlack = pit->second.colorKeyBlack; + } } const bool blendNeedsCutout = (blendMode == 1) || (blendMode >= 2 && !alphaCutout); const bool unlit = ((materialFlags & 0x01) != 0) || (blendMode >= 3); diff --git a/src/rendering/m2_renderer.cpp b/src/rendering/m2_renderer.cpp index d78845c6..46f82382 100644 --- a/src/rendering/m2_renderer.cpp +++ b/src/rendering/m2_renderer.cpp @@ -46,6 +46,7 @@ bool envFlagEnabled(const char* key, bool defaultValue) { static constexpr uint32_t kParticleFlagRandomized = 0x40; static constexpr uint32_t kParticleFlagTiled = 0x80; +static constexpr float kSmokeEmitInterval = 1.0f / 48.0f; float computeGroundDetailDownOffset(const M2ModelGPU& model, float scale) { // Keep a tiny sink to avoid hovering, but cap pivot compensation so details @@ -750,8 +751,7 @@ void M2Renderer::shutdown() { textureCache.clear(); textureCacheBytes_ = 0; textureCacheCounter_ = 0; - textureHasAlphaByPtr_.clear(); - textureColorKeyBlackByPtr_.clear(); + texturePropsByPtr_.clear(); failedTextureCache_.clear(); failedTextureRetryAt_.clear(); loggedTextureLoadFails_.clear(); @@ -1356,18 +1356,13 @@ bool M2Renderer::loadModel(const pipeline::M2Model& model, uint32_t modelId) { (!tcls.likelyFlame || modelLanternFamily)); bgpu.glowCardLike = bgpu.lanternGlowHint && tcls.hasGlowCardToken; bgpu.glowTint = tcls.glowTint; - bool texHasAlpha = false; if (tex != nullptr && tex != whiteTexture_.get()) { - auto ait = textureHasAlphaByPtr_.find(tex); - texHasAlpha = (ait != textureHasAlphaByPtr_.end()) ? ait->second : false; + auto pit = texturePropsByPtr_.find(tex); + if (pit != texturePropsByPtr_.end()) { + bgpu.hasAlpha = pit->second.hasAlpha; + bgpu.colorKeyBlack = pit->second.colorKeyBlack; + } } - bgpu.hasAlpha = texHasAlpha; - bool colorKeyBlack = false; - if (tex != nullptr && tex != whiteTexture_.get()) { - auto cit = textureColorKeyBlackByPtr_.find(tex); - colorKeyBlack = (cit != textureColorKeyBlackByPtr_.end()) ? cit->second : false; - } - bgpu.colorKeyBlack = colorKeyBlack; // textureCoordIndex is an index into a texture coord combo table, not directly // a UV set selector. Most batches have index=0 (UV set 0). We always use UV set 0 // since we don't have the full combo table — dual-UV effects are rare edge cases. @@ -1443,18 +1438,13 @@ bool M2Renderer::loadModel(const pipeline::M2Model& model, uint32_t modelId) { bgpu.indexStart = 0; bgpu.indexCount = gpuModel.indexCount; bgpu.texture = allTextures.empty() ? whiteTexture_.get() : allTextures[0]; - bool texHasAlpha = false; if (bgpu.texture != nullptr && bgpu.texture != whiteTexture_.get()) { - auto ait = textureHasAlphaByPtr_.find(bgpu.texture); - texHasAlpha = (ait != textureHasAlphaByPtr_.end()) ? ait->second : false; + auto pit = texturePropsByPtr_.find(bgpu.texture); + if (pit != texturePropsByPtr_.end()) { + bgpu.hasAlpha = pit->second.hasAlpha; + bgpu.colorKeyBlack = pit->second.colorKeyBlack; + } } - bgpu.hasAlpha = texHasAlpha; - bool colorKeyBlack = false; - if (bgpu.texture != nullptr && bgpu.texture != whiteTexture_.get()) { - auto cit = textureColorKeyBlackByPtr_.find(bgpu.texture); - colorKeyBlack = (cit != textureColorKeyBlackByPtr_.end()) ? cit->second : false; - } - bgpu.colorKeyBlack = colorKeyBlack; gpuModel.batches.push_back(bgpu); } @@ -1915,7 +1905,7 @@ void M2Renderer::update(float deltaTime, const glm::vec3& cameraPos, const glm:: std::uniform_real_distribution distDrift(-0.2f, 0.2f); smokeEmitAccum += deltaTime; - float emitInterval = 1.0f / 48.0f; // 48 particles per second per emitter (was 32; increased for denser lava/magma steam effects in sparse areas) + constexpr float emitInterval = kSmokeEmitInterval; // 48 particles per second per emitter if (smokeEmitAccum >= emitInterval && static_cast(smokeParticles.size()) < MAX_SMOKE_PARTICLES) { @@ -4285,8 +4275,7 @@ VkTexture* M2Renderer::loadTexture(const std::string& path, uint32_t texFlags) { textureCache[key] = std::move(e); failedTextureCache_.erase(key); failedTextureRetryAt_.erase(key); - textureHasAlphaByPtr_[texPtr] = hasAlpha; - textureColorKeyBlackByPtr_[texPtr] = colorKeyBlackHint; + texturePropsByPtr_[texPtr] = {hasAlpha, colorKeyBlackHint}; LOG_DEBUG("M2: Loaded texture: ", path, " (", blp.width, "x", blp.height, ")"); return texPtr; diff --git a/src/rendering/renderer.cpp b/src/rendering/renderer.cpp index 604ab932..638c60cb 100644 --- a/src/rendering/renderer.cpp +++ b/src/rendering/renderer.cpp @@ -5473,7 +5473,8 @@ void Renderer::renderWorld(game::World* world, game::GameHandler* gameHandler) { static const bool skipSky = (std::getenv("WOWEE_SKIP_SKY") != nullptr); // Get time of day for sky-related rendering - float timeOfDay = (skySystem && skySystem->getSkybox()) ? skySystem->getSkybox()->getTimeOfDay() : 12.0f; + auto* skybox = skySystem ? skySystem->getSkybox() : nullptr; + float timeOfDay = skybox ? skybox->getTimeOfDay() : 12.0f; // ── Multithreaded secondary command buffer recording ── // Terrain, WMO, and M2 record on worker threads while main thread handles @@ -6427,13 +6428,14 @@ void Renderer::renderReflectionPass() { bool canRenderScene = (vkCtx->getMsaaSamples() == VK_SAMPLE_COUNT_1_BIT); // Find dominant water height near camera - auto waterH = waterRenderer->getDominantWaterHeight(camera->getPosition()); + const glm::vec3 camPos = camera->getPosition(); + auto waterH = waterRenderer->getDominantWaterHeight(camPos); if (!waterH) return; float waterHeight = *waterH; // Skip reflection if camera is underwater (Z is up) - if (camera->getPosition().z < waterHeight + 0.5f) return; + if (camPos.z < waterHeight + 0.5f) return; // Compute reflected view and oblique projection glm::mat4 reflView = WaterRenderer::computeReflectedView(*camera, waterHeight); @@ -6448,7 +6450,7 @@ void Renderer::renderReflectionPass() { reflData.view = reflView; reflData.projection = reflProj; // Reflected camera position (Z is up) - glm::vec3 reflPos = camera->getPosition(); + glm::vec3 reflPos = camPos; reflPos.z = 2.0f * waterHeight - reflPos.z; reflData.viewPos = glm::vec4(reflPos, 1.0f); std::memcpy(reflPerFrameUBOMapped, &reflData, sizeof(GPUPerFrameData)); @@ -6460,7 +6462,8 @@ void Renderer::renderReflectionPass() { // Render scene into reflection texture (sky + terrain + WMO only for perf) if (skySystem) { rendering::SkyParams skyParams; - skyParams.timeOfDay = (skySystem->getSkybox()) ? skySystem->getSkybox()->getTimeOfDay() : 12.0f; + auto* reflSkybox = skySystem->getSkybox(); + skyParams.timeOfDay = reflSkybox ? reflSkybox->getTimeOfDay() : 12.0f; if (lightingManager) { const auto& lp = lightingManager->getLightingParams(); skyParams.directionalDir = lp.directionalDir; diff --git a/src/rendering/terrain_manager.cpp b/src/rendering/terrain_manager.cpp index c20801ae..ca2b6a1b 100644 --- a/src/rendering/terrain_manager.cpp +++ b/src/rendering/terrain_manager.cpp @@ -48,6 +48,10 @@ constexpr size_t ALPHA_MAP_PACKED = 2048; // 64×64 packed 4-bit alpha (half constexpr uint8_t ALPHA_FILL_FLAG = 0x80; // RLE command: fill vs. copy constexpr uint8_t ALPHA_COUNT_MASK = 0x7F; // RLE command: count bits +// Placement transform constants +constexpr float kDegToRad = 3.14159f / 180.0f; +constexpr float kInv1024 = 1.0f / 1024.0f; + int computeTerrainWorkerCount() { const char* raw = std::getenv("WOWEE_TERRAIN_WORKERS"); if (raw && *raw) { @@ -491,11 +495,11 @@ std::shared_ptr TerrainManager::prepareTile(int x, int y) { p.uniqueId = placement.uniqueId; p.position = glPos; p.rotation = glm::vec3( - -placement.rotation[2] * 3.14159f / 180.0f, - -placement.rotation[0] * 3.14159f / 180.0f, - (placement.rotation[1] + 180.0f) * 3.14159f / 180.0f + -placement.rotation[2] * kDegToRad, + -placement.rotation[0] * kDegToRad, + (placement.rotation[1] + 180.0f) * kDegToRad ); - p.scale = placement.scale / 1024.0f; + p.scale = placement.scale * kInv1024; pending->m2Placements.push_back(p); } @@ -561,9 +565,9 @@ std::shared_ptr TerrainManager::prepareTile(int x, int y) { placement.position[2]); glm::vec3 rot( - -placement.rotation[2] * 3.14159f / 180.0f, - -placement.rotation[0] * 3.14159f / 180.0f, - (placement.rotation[1] + 180.0f) * 3.14159f / 180.0f + -placement.rotation[2] * kDegToRad, + -placement.rotation[0] * kDegToRad, + (placement.rotation[1] + 180.0f) * kDegToRad ); // Pre-load WMO doodads (M2 models inside WMO) diff --git a/src/ui/game_screen.cpp b/src/ui/game_screen.cpp index bb5692c4..7f86b209 100644 --- a/src/ui/game_screen.cpp +++ b/src/ui/game_screen.cpp @@ -4540,6 +4540,8 @@ void GameScreen::renderTargetFrame(game::GameHandler& gameHandler) { char castLabel[72]; if (!castName.empty()) snprintf(castLabel, sizeof(castLabel), "%s (%.1fs)", castName.c_str(), castLeft); + else if (tspell != 0) + snprintf(castLabel, sizeof(castLabel), "Spell #%u (%.1fs)", tspell, castLeft); else snprintf(castLabel, sizeof(castLabel), "Casting... (%.1fs)", castLeft); { @@ -4709,8 +4711,12 @@ void GameScreen::renderTargetFrame(game::GameHandler& gameHandler) { ImGui::PopStyleColor(); } else { ImGui::PushStyleColor(ImGuiCol_Button, auraBorderColor); - char label[8]; - snprintf(label, sizeof(label), "%u", aura.spellId); + const std::string& tAuraName = gameHandler.getSpellName(aura.spellId); + char label[32]; + if (!tAuraName.empty()) + snprintf(label, sizeof(label), "%.6s", tAuraName.c_str()); + else + snprintf(label, sizeof(label), "%u", aura.spellId); ImGui::Button(label, ImVec2(ICON_SIZE, ICON_SIZE)); ImGui::PopStyleColor(); } @@ -15449,8 +15455,12 @@ void GameScreen::renderBuffBar(game::GameHandler& gameHandler) { ImGui::PopStyleColor(); } else { ImGui::PushStyleColor(ImGuiCol_Button, borderColor); - char label[8]; - snprintf(label, sizeof(label), "%u", aura.spellId); + const std::string& pAuraName = gameHandler.getSpellName(aura.spellId); + char label[32]; + if (!pAuraName.empty()) + snprintf(label, sizeof(label), "%.6s", pAuraName.c_str()); + else + snprintf(label, sizeof(label), "%u", aura.spellId); ImGui::Button(label, ImVec2(ICON_SIZE, ICON_SIZE)); ImGui::PopStyleColor(); } @@ -24142,7 +24152,13 @@ void GameScreen::renderWhoWindow(game::GameHandler& gameHandler) { ImGui::TableSetColumnIndex(4); if (e.zoneId != 0) { std::string zoneName = gameHandler.getWhoAreaName(e.zoneId); - ImGui::TextUnformatted(zoneName.empty() ? "Unknown" : zoneName.c_str()); + if (!zoneName.empty()) + ImGui::TextUnformatted(zoneName.c_str()); + else { + char zfb[32]; + snprintf(zfb, sizeof(zfb), "Zone #%u", e.zoneId); + ImGui::TextUnformatted(zfb); + } } ImGui::PopID();