mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-28 09:33:52 +00:00
perf: constexpr reciprocals, cache redundant lookups, consolidate texture maps
- Hoist DBC field index lookups before loops in game_handler (7 DBC iteration loops) - Cache getSkybox()/getPosition() calls instead of redundant per-frame queries - Merge textureHasAlphaByPtr_ + textureColorKeyBlackByPtr_ into single map - Add constexpr for DEG_TO_RAD, reciprocal constants, physics delta - Add reserve() for WMO/M2 collision grid queries and portal BFS - Frustum plane normalize: inversesqrt instead of length+divide - M2 particle emission: inversesqrt for direction normalization - Parse creature display IDs from query response - UI: show spell names/IDs as fallback instead of "Unknown"
This commit is contained in:
parent
b0466e9029
commit
d26eed1e7c
9 changed files with 153 additions and 104 deletions
|
|
@ -293,8 +293,11 @@ private:
|
|||
bool normalMapPending = false; // deferred normal map generation
|
||||
};
|
||||
std::unordered_map<std::string, TextureCacheEntry> textureCache;
|
||||
std::unordered_map<VkTexture*, bool> textureHasAlphaByPtr_;
|
||||
std::unordered_map<VkTexture*, bool> textureColorKeyBlackByPtr_;
|
||||
struct TextureProperties {
|
||||
bool hasAlpha = false;
|
||||
bool colorKeyBlack = false;
|
||||
};
|
||||
std::unordered_map<VkTexture*, TextureProperties> texturePropsByPtr_;
|
||||
std::unordered_map<std::string, VkTexture*> compositeCache_; // key → texture for reuse
|
||||
std::unordered_set<std::string> failedTextureCache_; // negative cache for budget exhaustion
|
||||
std::unordered_map<std::string, uint64_t> failedTextureRetryAt_;
|
||||
|
|
|
|||
|
|
@ -483,8 +483,11 @@ private:
|
|||
bool colorKeyBlack = false;
|
||||
};
|
||||
std::unordered_map<std::string, TextureCacheEntry> textureCache;
|
||||
std::unordered_map<VkTexture*, bool> textureHasAlphaByPtr_;
|
||||
std::unordered_map<VkTexture*, bool> textureColorKeyBlackByPtr_;
|
||||
struct TextureProperties {
|
||||
bool hasAlpha = false;
|
||||
bool colorKeyBlack = false;
|
||||
};
|
||||
std::unordered_map<VkTexture*, TextureProperties> texturePropsByPtr_;
|
||||
size_t textureCacheBytes_ = 0;
|
||||
uint64_t textureCacheCounter_ = 0;
|
||||
size_t textureCacheBudgetBytes_ = 2048ull * 1024 * 1024;
|
||||
|
|
|
|||
|
|
@ -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<int32_t>(dbc->getUInt32(i, f0));
|
||||
if (f1 != 0xFFFFFFFF) entry.effectBasePoints[1] = static_cast<int32_t>(dbc->getUInt32(i, f1));
|
||||
if (f2 != 0xFFFFFFFF) entry.effectBasePoints[2] = static_cast<int32_t>(dbc->getUInt32(i, f2));
|
||||
}
|
||||
if (ebp0Field != 0xFFFFFFFF) entry.effectBasePoints[0] = static_cast<int32_t>(dbc->getUInt32(i, ebp0Field));
|
||||
if (ebp1Field != 0xFFFFFFFF) entry.effectBasePoints[1] = static_cast<int32_t>(dbc->getUInt32(i, ebp1Field));
|
||||
if (ebp2Field != 0xFFFFFFFF) entry.effectBasePoints[2] = static_cast<int32_t>(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<float>(dbc->getUInt32(i, durF)); // store index temporarily
|
||||
}
|
||||
if (durIdxField != 0xFFFFFFFF)
|
||||
entry.durationSec = static_cast<float>(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<uint8_t>(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<uint8_t>(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;
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ namespace rendering {
|
|||
|
||||
namespace {
|
||||
|
||||
constexpr float kMaxPhysicsDelta = 1.0f / 30.0f;
|
||||
|
||||
std::optional<float> selectReachableFloor(const std::optional<float>& terrainH,
|
||||
const std::optional<float>& 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_) {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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<float> 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<int>(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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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<PendingTile> 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<PendingTile> 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)
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue