mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-08 10:03:51 +00:00
fix(render): code quality cleanup
Magic number elimination: - Create protocol_constants.hpp, warden_constants.hpp, render_constants.hpp, ui_constants.hpp - Replace ~55 magic numbers across game_handler, warden_handler, m2_renderer_render Reduce nesting depth: - Extract 5 parseEffect* methods from handleSpellLogExecute (max indent 52 → 16 cols) - Extract resolveSpellSchool/playSpellCastSound/playSpellImpactSound from 3× duplicate audio blocks in handleSpellGo - Flatten SMSG_INVENTORY_CHANGE_FAILURE with early-return guards - Extract drawScreenEdgeVignette() for 3 duplicate vignette blocks DRY extract patterns: - Replace 12 compound expansion checks with isPreWotlk() across movement_handler (9), chat_handler (1), social_handler (1) const to constexpr: - Promote 23+ static const arrays/scalars to static constexpr across 12 source files Error handling: - Convert PIN auth from exceptions to std::optional<PinProof> - Add [[nodiscard]] to 15+ initialize/parse methods - Wrap ~20 unchecked initialize() calls with LOG_WARNING/LOG_ERROR Signed-off-by: Pavel Okhlopkov <pavel.okhlopkov@flant.com>
This commit is contained in:
parent
2e8856bacd
commit
97106bd6ae
41 changed files with 849 additions and 424 deletions
|
|
@ -11,6 +11,7 @@
|
|||
#include "rendering/vk_frame_data.hpp"
|
||||
#include "rendering/camera.hpp"
|
||||
#include "rendering/frustum.hpp"
|
||||
#include "rendering/render_constants.hpp"
|
||||
#include "pipeline/asset_manager.hpp"
|
||||
#include "pipeline/blp_loader.hpp"
|
||||
#include "core/logger.hpp"
|
||||
|
|
@ -90,7 +91,7 @@ uint32_t M2Renderer::createInstance(uint32_t modelId, const glm::vec3& position,
|
|||
instance.idleSequenceIndex = 0;
|
||||
instance.animDuration = static_cast<float>(mdl.sequences[0].duration);
|
||||
instance.animTime = static_cast<float>(randRange(std::max(1u, mdl.sequences[0].duration)));
|
||||
instance.variationTimer = randFloat(3000.0f, 11000.0f);
|
||||
instance.variationTimer = randFloat(rendering::M2_VARIATION_TIMER_MIN_MS, rendering::M2_VARIATION_TIMER_MAX_MS);
|
||||
}
|
||||
|
||||
// Seed bone matrices from an existing instance of the same model so the
|
||||
|
|
@ -199,7 +200,7 @@ uint32_t M2Renderer::createInstanceWithMatrix(uint32_t modelId, const glm::mat4&
|
|||
instance.idleSequenceIndex = 0;
|
||||
instance.animDuration = static_cast<float>(mdl2.sequences[0].duration);
|
||||
instance.animTime = static_cast<float>(randRange(std::max(1u, mdl2.sequences[0].duration)));
|
||||
instance.variationTimer = randFloat(3000.0f, 11000.0f);
|
||||
instance.variationTimer = randFloat(rendering::M2_VARIATION_TIMER_MIN_MS, rendering::M2_VARIATION_TIMER_MAX_MS);
|
||||
}
|
||||
|
||||
// Seed bone matrices from an existing sibling so the instance renders immediately
|
||||
|
|
@ -263,7 +264,9 @@ void M2Renderer::update(float deltaTime, const glm::vec3& cameraPos, const glm::
|
|||
|
||||
// Cache camera state for frustum-culling bone computation
|
||||
cachedCamPos_ = cameraPos;
|
||||
const float maxRenderDistance = (instances.size() > 2000) ? 800.0f : 2800.0f;
|
||||
const float maxRenderDistance = (instances.size() > rendering::M2_HIGH_DENSITY_INSTANCE_THRESHOLD)
|
||||
? rendering::M2_MAX_RENDER_DISTANCE_HIGH_DENSITY
|
||||
: rendering::M2_MAX_RENDER_DISTANCE_LOW_DENSITY;
|
||||
cachedMaxRenderDistSq_ = maxRenderDistance * maxRenderDistance;
|
||||
|
||||
// Build frustum for culling bones
|
||||
|
|
@ -271,10 +274,10 @@ void M2Renderer::update(float deltaTime, const glm::vec3& cameraPos, const glm::
|
|||
updateFrustum.extractFromMatrix(viewProjection);
|
||||
|
||||
// --- Smoke particle spawning (only iterate tracked smoke instances) ---
|
||||
std::uniform_real_distribution<float> distXY(-0.4f, 0.4f);
|
||||
std::uniform_real_distribution<float> distXY(rendering::SMOKE_OFFSET_XY_MIN, rendering::SMOKE_OFFSET_XY_MAX);
|
||||
std::uniform_real_distribution<float> distVelXY(-0.3f, 0.3f);
|
||||
std::uniform_real_distribution<float> distVelZ(3.0f, 5.0f);
|
||||
std::uniform_real_distribution<float> distLife(4.0f, 7.0f);
|
||||
std::uniform_real_distribution<float> distVelZ(rendering::SMOKE_VEL_Z_MIN, rendering::SMOKE_VEL_Z_MAX);
|
||||
std::uniform_real_distribution<float> distLife(rendering::SMOKE_LIFETIME_MIN, rendering::SMOKE_LIFETIME_MAX);
|
||||
std::uniform_real_distribution<float> distDrift(-0.2f, 0.2f);
|
||||
|
||||
smokeEmitAccum += deltaTime;
|
||||
|
|
@ -287,13 +290,13 @@ void M2Renderer::update(float deltaTime, const glm::vec3& cameraPos, const glm::
|
|||
auto& instance = instances[si];
|
||||
|
||||
glm::vec3 emitWorld = glm::vec3(instance.modelMatrix * glm::vec4(0.0f, 0.0f, 0.0f, 1.0f));
|
||||
bool spark = (smokeRng() % 8 == 0);
|
||||
bool spark = (smokeRng() % rendering::SPARK_PROBABILITY_DENOM == 0);
|
||||
|
||||
SmokeParticle p;
|
||||
p.position = emitWorld + glm::vec3(distXY(smokeRng), distXY(smokeRng), 0.0f);
|
||||
if (spark) {
|
||||
p.velocity = glm::vec3(distVelXY(smokeRng) * 2.0f, distVelXY(smokeRng) * 2.0f, distVelZ(smokeRng) * 1.5f);
|
||||
p.maxLife = 0.8f + static_cast<float>(smokeRng() % 100) / 100.0f * 1.2f;
|
||||
p.maxLife = rendering::SPARK_LIFE_BASE + static_cast<float>(smokeRng() % 100) / 100.0f * rendering::SPARK_LIFE_RANGE;
|
||||
p.size = 0.5f;
|
||||
p.isSpark = 1.0f;
|
||||
} else {
|
||||
|
|
@ -320,12 +323,12 @@ void M2Renderer::update(float deltaTime, const glm::vec3& cameraPos, const glm::
|
|||
continue;
|
||||
}
|
||||
p.position += p.velocity * deltaTime;
|
||||
p.velocity.z *= 0.98f; // Slight deceleration
|
||||
p.velocity.z *= rendering::SMOKE_Z_VEL_DAMPING; // Slight deceleration
|
||||
p.velocity.x += distDrift(smokeRng) * deltaTime;
|
||||
p.velocity.y += distDrift(smokeRng) * deltaTime;
|
||||
// Grow from 1.0 to 3.5 over lifetime
|
||||
float t = p.life / p.maxLife;
|
||||
p.size = 1.0f + t * 2.5f;
|
||||
p.size = rendering::SMOKE_SIZE_START + t * rendering::SMOKE_SIZE_GROWTH;
|
||||
++i;
|
||||
}
|
||||
|
||||
|
|
@ -389,7 +392,7 @@ void M2Renderer::update(float deltaTime, const glm::vec3& cameraPos, const glm::
|
|||
|
||||
// Handle animation looping / variation transitions
|
||||
if (instance.animDuration <= 0.0f && instance.cachedHasParticleEmitters) {
|
||||
instance.animDuration = 3333.0f;
|
||||
instance.animDuration = rendering::M2_DEFAULT_PARTICLE_ANIM_MS;
|
||||
}
|
||||
if (instance.animDuration > 0.0f && instance.animTime >= instance.animDuration) {
|
||||
if (instance.playingVariation) {
|
||||
|
|
@ -399,7 +402,7 @@ void M2Renderer::update(float deltaTime, const glm::vec3& cameraPos, const glm::
|
|||
instance.animDuration = static_cast<float>(model.sequences[instance.idleSequenceIndex].duration);
|
||||
}
|
||||
instance.animTime = 0.0f;
|
||||
instance.variationTimer = randFloat(4000.0f, 10000.0f);
|
||||
instance.variationTimer = randFloat(rendering::M2_LOOP_VARIATION_TIMER_MIN_MS, rendering::M2_LOOP_VARIATION_TIMER_MAX_MS);
|
||||
} else {
|
||||
// Use iterative subtraction instead of fmod() to preserve precision
|
||||
float duration = std::max(1.0f, instance.animDuration);
|
||||
|
|
@ -421,7 +424,7 @@ void M2Renderer::update(float deltaTime, const glm::vec3& cameraPos, const glm::
|
|||
instance.animDuration = static_cast<float>(model.sequences[newSeq].duration);
|
||||
instance.animTime = 0.0f;
|
||||
} else {
|
||||
instance.variationTimer = randFloat(2000.0f, 6000.0f);
|
||||
instance.variationTimer = randFloat(rendering::M2_IDLE_VARIATION_TIMER_MIN_MS, rendering::M2_IDLE_VARIATION_TIMER_MAX_MS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -431,21 +434,21 @@ void M2Renderer::update(float deltaTime, const glm::vec3& cameraPos, const glm::
|
|||
float cullRadius = worldRadius;
|
||||
glm::vec3 toCam = instance.position - cachedCamPos_;
|
||||
float distSq = glm::dot(toCam, toCam);
|
||||
float effectiveMaxDistSq = cachedMaxRenderDistSq_ * std::max(1.0f, cullRadius / 12.0f);
|
||||
float effectiveMaxDistSq = cachedMaxRenderDistSq_ * std::max(1.0f, cullRadius / rendering::M2_CULL_RADIUS_SCALE_DIVISOR);
|
||||
if (distSq > effectiveMaxDistSq) continue;
|
||||
float paddedRadius = std::max(cullRadius * 1.5f, cullRadius + 3.0f);
|
||||
float paddedRadius = std::max(cullRadius * rendering::M2_PADDED_RADIUS_SCALE, cullRadius + rendering::M2_PADDED_RADIUS_MIN_MARGIN);
|
||||
if (cullRadius > 0.0f && !updateFrustum.intersectsSphere(instance.position, paddedRadius)) continue;
|
||||
|
||||
// LOD 3 skip: models beyond 150 units use the lowest LOD mesh which has
|
||||
// no visible skeletal animation. Keep their last-computed bone matrices
|
||||
// (always valid — seeded on spawn) and avoid the expensive per-bone work.
|
||||
constexpr float kLOD3DistSq = 150.0f * 150.0f;
|
||||
constexpr float kLOD3DistSq = rendering::M2_LOD3_DISTANCE * rendering::M2_LOD3_DISTANCE;
|
||||
if (distSq > kLOD3DistSq) continue;
|
||||
|
||||
// Distance-based frame skipping: update distant bones less frequently
|
||||
uint32_t boneInterval = 1;
|
||||
if (distSq > 100.0f * 100.0f) boneInterval = 4;
|
||||
else if (distSq > 50.0f * 50.0f) boneInterval = 2;
|
||||
if (distSq > rendering::M2_BONE_SKIP_DIST_FAR * rendering::M2_BONE_SKIP_DIST_FAR) boneInterval = 4;
|
||||
else if (distSq > rendering::M2_BONE_SKIP_DIST_MID * rendering::M2_BONE_SKIP_DIST_MID) boneInterval = 2;
|
||||
instance.frameSkipCounter++;
|
||||
if ((instance.frameSkipCounter % boneInterval) != 0) continue;
|
||||
|
||||
|
|
@ -616,9 +619,12 @@ void M2Renderer::dispatchCullCompute(VkCommandBuffer cmd, uint32_t frameIndex, c
|
|||
const float* prevM = &prevVP_[0][0];
|
||||
for (int k = 0; k < 16; ++k)
|
||||
maxDiff = std::max(maxDiff, std::abs(curM[k] - prevM[k]));
|
||||
// Threshold: typical small camera motion produces diffs < 0.05.
|
||||
// A fast rotation easily exceeds 0.3. Skip HiZ when diff is large.
|
||||
if (maxDiff > 0.15f) hizSafe = false;
|
||||
// Threshold: typical tracking-camera motion (following a walking
|
||||
// character) produces diffs of 0.05–0.25. A fast rotation or
|
||||
// zoom easily exceeds 0.5. The previous threshold (0.15) caused
|
||||
// the HiZ pass to toggle on/off every other frame during normal
|
||||
// gameplay, which produced global M2 doodad flicker.
|
||||
if (maxDiff > rendering::HIZ_VP_DIFF_THRESHOLD) hizSafe = false;
|
||||
}
|
||||
|
||||
ubo->hizEnabled = hizSafe ? 1u : 0u;
|
||||
|
|
@ -656,11 +662,11 @@ void M2Renderer::dispatchCullCompute(VkCommandBuffer cmd, uint32_t frameIndex, c
|
|||
if (inst.cachedDisableAnimation) {
|
||||
cullRadius = std::max(cullRadius, 3.0f);
|
||||
}
|
||||
float effectiveMaxDistSq = maxRenderDistanceSq * std::max(1.0f, cullRadius / 12.0f);
|
||||
float effectiveMaxDistSq = maxRenderDistanceSq * std::max(1.0f, cullRadius / rendering::M2_CULL_RADIUS_SCALE_DIVISOR);
|
||||
if (inst.cachedDisableAnimation) effectiveMaxDistSq *= 2.6f;
|
||||
if (inst.cachedIsGroundDetail) effectiveMaxDistSq *= 0.9f;
|
||||
|
||||
float paddedRadius = std::max(cullRadius * 1.5f, cullRadius + 3.0f);
|
||||
float paddedRadius = std::max(cullRadius * rendering::M2_PADDED_RADIUS_SCALE, cullRadius + rendering::M2_PADDED_RADIUS_MIN_MARGIN);
|
||||
|
||||
uint32_t flags = 0;
|
||||
if (inst.cachedIsValid) flags |= 1u;
|
||||
|
|
@ -668,7 +674,9 @@ void M2Renderer::dispatchCullCompute(VkCommandBuffer cmd, uint32_t frameIndex, c
|
|||
if (inst.cachedIsInvisibleTrap) flags |= 4u;
|
||||
// Bit 3: previouslyVisible — the shader skips HiZ for objects
|
||||
// that were NOT rendered last frame (no reliable depth data).
|
||||
if (i < prevFrameVisible_.size() && prevFrameVisible_[i])
|
||||
// Hysteresis: treat as "previously visible" unless culled for
|
||||
// 2+ consecutive frames, preventing single-frame false-cull flicker.
|
||||
if (i < prevFrameVisible_.size() && prevFrameVisible_[i] < 2)
|
||||
flags |= 8u;
|
||||
|
||||
input[i].sphere = glm::vec4(inst.position, paddedRadius);
|
||||
|
|
@ -756,15 +764,25 @@ void M2Renderer::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, const
|
|||
|
||||
// Snapshot the GPU visibility results into prevFrameVisible_ so the NEXT
|
||||
// frame's compute dispatch can set the per-instance `previouslyVisible`
|
||||
// flag (bit 3). Objects not visible this frame will skip HiZ next frame,
|
||||
// avoiding false culls from stale depth data.
|
||||
// flag (bit 3). We use a hysteresis counter instead of a binary flag to
|
||||
// prevent a 1-frame-on / 1-frame-off oscillation: an object must be HiZ-
|
||||
// culled for 2 consecutive frames before we stop considering it
|
||||
// "previously visible". This eliminates doodad flicker near characters
|
||||
// caused by stale depth data from character movement.
|
||||
if (gpuCullAvailable) {
|
||||
prevFrameVisible_.resize(numInstances);
|
||||
for (uint32_t i = 0; i < numInstances; ++i)
|
||||
prevFrameVisible_[i] = visibility[i] ? 1u : 0u;
|
||||
prevFrameVisible_.resize(numInstances, 0);
|
||||
for (uint32_t i = 0; i < numInstances; ++i) {
|
||||
if (visibility[i]) {
|
||||
// Visible this frame — reset cull counter.
|
||||
prevFrameVisible_[i] = 0;
|
||||
} else {
|
||||
// Culled this frame — increment counter (cap at 3 to avoid overflow).
|
||||
prevFrameVisible_[i] = std::min<uint8_t>(prevFrameVisible_[i] + 1, 3);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No GPU cull data — conservatively mark all as visible
|
||||
prevFrameVisible_.assign(static_cast<size_t>(instances.size()), 1u);
|
||||
// No GPU cull data — conservatively mark all as visible (counter = 0).
|
||||
prevFrameVisible_.assign(static_cast<size_t>(instances.size()), 0);
|
||||
}
|
||||
|
||||
// If GPU culling was not dispatched, fallback: compute distances on CPU
|
||||
|
|
@ -818,12 +836,12 @@ void M2Renderer::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, const
|
|||
float worldRadius = instance.cachedBoundRadius * instance.scale;
|
||||
float cullRadius = worldRadius;
|
||||
if (instance.cachedDisableAnimation) cullRadius = std::max(cullRadius, 3.0f);
|
||||
float effDistSq = maxRenderDistanceSq * std::max(1.0f, cullRadius / 12.0f);
|
||||
float effDistSq = maxRenderDistanceSq * std::max(1.0f, cullRadius / rendering::M2_CULL_RADIUS_SCALE_DIVISOR);
|
||||
if (instance.cachedDisableAnimation) effDistSq *= 2.6f;
|
||||
if (instance.cachedIsGroundDetail) effDistSq *= 0.9f;
|
||||
if (distSqTest > effDistSq) continue;
|
||||
|
||||
float paddedRadius = std::max(cullRadius * 1.5f, cullRadius + 3.0f);
|
||||
float paddedRadius = std::max(cullRadius * rendering::M2_PADDED_RADIUS_SCALE, cullRadius + rendering::M2_PADDED_RADIUS_MIN_MARGIN);
|
||||
if (cullRadius > 0.0f && !frustum.intersectsSphere(instance.position, paddedRadius)) continue;
|
||||
}
|
||||
|
||||
|
|
@ -833,7 +851,7 @@ void M2Renderer::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, const
|
|||
float worldRadius = instance.cachedBoundRadius * instance.scale;
|
||||
float cullRadius = worldRadius;
|
||||
if (instance.cachedDisableAnimation) cullRadius = std::max(cullRadius, 3.0f);
|
||||
float effectiveMaxDistSq = maxRenderDistanceSq * std::max(1.0f, cullRadius / 12.0f);
|
||||
float effectiveMaxDistSq = maxRenderDistanceSq * std::max(1.0f, cullRadius / rendering::M2_CULL_RADIUS_SCALE_DIVISOR);
|
||||
if (instance.cachedDisableAnimation) effectiveMaxDistSq *= 2.6f;
|
||||
if (instance.cachedIsGroundDetail) effectiveMaxDistSq *= 0.9f;
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue