Classify light shafts, portals, spotlights, bubbles, and similar M2
doodads as spell effects so they render with additive blending instead
of as solid opaque objects.
Set camera yaw from server orientation on world load so teleports face
the correct direction.
Reduce area trigger minimum radius (3.0 sphere, 4.0 box) to prevent
premature portal firing near tram entrances.
WMO interior doodads (gears, decorations) were blocking player movement
via M2 collision. Skip collision for all WMO doodad M2 instances since
the WMO itself handles wall collision.
Also filter WMO wall collision using MOPY per-triangle flags: only
rendered+collidable triangles block the player, skipping invisible
collision hulls.
Revert tram portal extended range (no longer needed with collision fix).
- NPC hair/skin textures now use per-instance overrides instead of shared
model-level textures, so each NPC shows its own hair color/style
- Hair/skin DBC lookup runs for every NPC instance (including cached models)
rather than only on first load
- Fix keyframe binary search to use float comparison matching original
linear scan semantics
- Replace O(n) linear keyframe search with O(log n) binary search in both
M2 and Character renderers (runs thousands of times per frame)
- Smoke particle removal: swap-and-pop instead of O(n²) vector erase
- Character render backface cull: eliminate sqrt via squared comparison
- Quaternion validation: use length² instead of sqrt-based length check
Use cached model flags (isValid, isSmoke, isInvisibleTrap, isGroundDetail,
disableAnimation, boundRadius) on M2Instance instead of models.find() in
the hot culling paths. Also complete cached flag initialization in
createInstanceWithMatrix().
- Glow sprites now use dedicated vertex buffer (glowVB_) separate from
M2 particle buffer to prevent data race when renderM2Particles()
overwrites glow data mid-flight
- Move fadeAlpha from shared material UBO to per-draw push constants,
eliminating cross-instance alpha race on non-double-buffered UBOs
- Smooth adaptive render distance transitions to prevent pop-in/out
at instance count thresholds (1000/2000)
- Distance-tiered character bone throttling: near (<30u) every frame,
mid (30-60u) every 3rd, far (60-120u) every 6th frame
- Skip weapon instance animation updates (transforms set by parent bones)
- Split M2 instances into fast-path index lists (animated, particle-only,
particle-all, smoke) to avoid iterating all 46K instances per frame
- Cache model flags (hasAnimation, disableAnimation, isSmoke, etc.) on
M2Instance struct to eliminate per-frame hash lookups
- Replace full rebuildSpatialIndex on position/transform updates with
incremental grid cell remove+add, preventing 8.5ms/frame rebuild cost
- Advance animTime for all instances (texture UV animation) but only
compute bones and particles for the ~3K that need it
M2_UPDATE: 10.7ms → 2.0ms, FPS: 35 → 55-59
Reset descriptor pools in CharacterRenderer/M2Renderer/WMORenderer on map
change to prevent VK_ERROR_DEVICE_LOST from pool exhaustion. Defer re-entrant
SMSG_NEW_WORLD during active world load to avoid recursive cleanup crashes.
Gate swim bubbles on swimming state, skip redundant shadow pipeline re-init,
add WOWEE_SKIP_* env vars for render isolation debugging.
StormLib's bundled libtomcrypt uses x86 inline assembly (bswapl/movl)
gated by __MINGW32__, which is defined on CLANGARM64 too. Pass
-DLTC_NO_BSWAP to force portable C byte-swap fallback.
Replace monolithic finalizeTile() with a phased state machine that spreads
GPU upload work across multiple frames (TERRAIN→M2→WMO→WATER→AMBIENT→DONE).
Each advanceFinalization() call does one bounded unit of work within the
per-frame time budget, eliminating 50-300ms frame hitches when entering cities.
Additional performance improvements:
- Pre-allocate bone SSBOs at M2 instance creation instead of lazily during
first render frame, preventing hitches when many skinned characters appear
- Enable WMO distance culling (800 units) with active-group exemption so
the player's current floor/neighbors are never culled
- Add 4-tier adaptive M2 render distance (250/400/600/1000 based on count)
- Remove dead PendingM2Upload queue code superseded by incremental system
Fix tile re-enqueueing bug: keep tiles in pendingTiles until committed to
loadedTiles (not when moved to finalizingTiles_) so streamTiles() doesn't
re-enqueue tiles mid-finalization. Also handle unloadTile() for tiles in
the finalizingTiles_ deque to prevent orphaned water/M2/WMO resources.
When M2Renderer's descriptor pool was exhausted, batch.materialSet
would be VK_NULL_HANDLE and the bind was skipped, but the draw call
still executed using the previously bound descriptor set from
CharacterRenderer — causing game objects to render with the player's
skin/armor textures. Skip the entire batch instead.
Fireflies, dragonflies, butterflies, and moths no longer get
disableAnimation/foliage classification so their bone animation
and particles run normally.
- Spawn dark point-sprite insects buzzing around cattails/reeds/kelp/seaweed
- Fix firefly M2 particles: exempt from alpha dampening and forced gravity
- Make water shoreline/crest foam more irregular with UV warping and bluer tint
Remove negative cache for transient load failures in M2, terrain, and
character renderers. Failed textures return white and will be retried
on next model/tile load when assets may have finished streaming.
- Cache material UBO mapped pointers at creation time, eliminating
per-batch vmaGetAllocationInfo() calls in the hot render path
- Precompute foliage/elven/lantern/kobold model name classifications
at load time instead of per-instance string operations every frame
- Remove redundant descriptor set and push constant rebinds on WMO
pipeline switches (preserved across compatible layouts)
- Pre-allocate glow sprite descriptor set once at init instead of
allocating from the pool every frame
Shadow casting: foliage batches now bind their actual texture in the shadow
pass with alpha testing, producing leaf-shaped shadows instead of solid cards.
Uses a per-frame resettable descriptor pool for texture sets.
Shadow receiving: foliage fragments now sample the shadow map with PCF
instead of using a flat constant darkening.
Gameobject M2 instances (books, crates, chests) were continuously
cycling their animations because M2Renderer unconditionally loops
all sequences. Added setInstanceAnimationFrozen() and freeze all
gameobject instances at creation time so they stay in their bind pose.
removeInstance() and removeInstances() were erasing M2Instances without
calling destroyInstanceBones(), leaking VMA bone buffers permanently.
This caused framerate to drop and never recover after NPC encounters.
All three shadow renderers (WMO, M2, Character) were iterating every
loaded instance with zero culling. Now skip instances outside the
180-unit shadow frustum radius via squared-distance check.
Raise all texture cache defaults from 1GB to 4GB to reduce rejections.
Cap cache-full warnings (texture + model) to 3 messages per renderer,
and cap update block parse errors to 5 messages.
Strip per-frame/periodic logging from CharacterRenderer (batch dump,
instance count) and M2Renderer (frame timing profiler) to avoid
unnecessary string formatting and I/O in render loops.
- Add error handling: revert to 1x if recreateSwapchain fails
- Clamp requested MSAA to device maximum before applying
- Retry MSAA color image allocation without TRANSIENT on failure
- Remove redundant vkDeviceWaitIdle from WMO/M2/Character recreatePipelines
(caller already waits once, was causing ~13 stalls instead of 1)
- ensure player transform sync is not gated by third-person so player shadow stays consistent
- hold shadow projection center during movement and snap once on stop to remove delayed catch-up
- smooth foliage caster transitions using blended phase-shifted UV samples
- tune foliage motion frequencies/amplitudes for less popping
- increase shadow map resolution to 2048 and retune terrain PCF texel scale
- increase global shadow strength from 0.62 to 0.68 for stronger, clearer shadows
- keep shadow projection center fixed while moving to remove per-frame projection churn flicker
- replace delayed post-move catch-up with immediate stop transition and idle smoothing
- rework foliage shadow caster motion to use blended phase-shifted UV samples for continuous position transitions
- reduce high-frequency foliage threshold popping by removing threshold warping path
- sharpen terrain receive filtering with tuned 5-tap PCF weights/offset for more detailed shadows
- raise shadow map resolution to 1536 and keep light-space texel snapping for stable sampling
- set shadows enabled by default and lower global shadow strength from 0.65 to 0.62
- keep foliage animation speed consistent between moving and idle at 80%
- reduce per-tile ground clutter generation pressure and enforce tighter caps to avoid spikes
- remove expensive detail dedupe scans from the hot render path
- add progressive/lazy clutter updates around player movement to smooth frame pacing
- lower noisy runtime INFO logging to DEBUG/throttled paths
- keep terrain/game screen updates responsive while preserving existing behavior
Hide NPC cloak/object-skin mesh when no cape texture resolves by using a transparent texture fallback, preventing skin-texture bleed on cloaks. Tighten NPC equipment region compositing by slot and add safe humanoid geoset selection to avoid robe-over-pants conflicts and odd pants texturing.
Reduce login/runtime hitching by deferring non-critical world-system initialization across frames, lowering per-frame transport doodad spawn budget, and demoting high-volume transport/MO_TRANSPORT diagnostics to debug. Gate M2 glow diagnostics behind WOWEE_M2_GLOW_DIAG and make zone music prewarm opt-in via WOWEE_PREWARM_ZONE_MUSIC.
Replace 2D screen-space ding rings with real WoW LevelUp.m2 particle/geometry
effect. Fix FBlock particle color parsing (C3Vector floats, not CImVector bytes)
which was producing blue/red instead of golden yellow. Spell effect models bypass
particle dampeners, glow sprite conversion, Mod→Additive blend override, and all
collision (floor/wall/camera) to prevent camera zoom-in. Other players' level-ups
trigger the 3D effect at their position with group chat notification. F7 hotkey
for testing.
Mod blend (GL_DST_COLOR, GL_ZERO) multiplies the framebuffer by texture color,
so dark pixels create visible black rectangles. Use a variable colorKeyThreshold
uniform (0.7 for Mod/Mod2x, 0.08 default) to discard dark pixels from these
batches while preserving normal colorKeyBlack behavior elsewhere.
The pixel content glow detection (>60% dark = glow) was too aggressive,
flagging dark metal textures on sconces as glow textures and making
structural geometry transparent. The forced additive blending for
colorKeyBlack batches compounded the issue.
Reverted both. Added per-batch diagnostic logging for models containing
"light", "lamp", or "lantern" to identify the actual blend modes and
material flags on Stormwind bridge lamps.
force additive blending for colorKeyBlack batches
Two key changes:
1. Detect glow-like textures by analyzing pixel content during loading:
textures that are >60% near-black with some bright pixels are flagged
as colorKeyBlack regardless of filename. This catches glow textures
like Stormwind street lamps whose paths don't contain keywords like
"lamp" or "lantern".
2. Force additive blending (mode 3) for batches with colorKeyBlack
textures that have blendMode 1 or 2. These textures are designed for
additive blending where black = transparent, but some M2 files specify
Alpha or AlphaKey blend modes which cause black areas to render as
solid dark disks instead of being invisible.
Two fixes:
1. shouldUseGlowSprite now requires the UNLIT material flag (0x01) for
the colorKeyBlack+flameLikeModel path. Previously, structural geometry
(torch handles, sconce brackets) on flame-like models got replaced
with glow sprites because their texture paths contained keywords like
"torch" in the directory name, setting colorKeyBlack on non-glow
textures. Requiring UNLIT ensures only actual glow/emissive batches
become sprites while lit structural geometry renders normally.
2. Fragment shader now discards near-black (maxRGB < 0.1) for ALL unlit
non-opaque batches, not just additive blend modes. Glow effects on
lanterns/lamps that use blendMode 1 (AlphaKey) or 2 (Alpha) instead
of 3 (Additive) now properly discard their black backgrounds.
Three-part fix for glow textures showing opaque black rectangles instead
of being transparent:
1. Pass blend mode to fragment shader via uBlendMode uniform. For additive
blend modes (3=Add, 6=BlendAdd), discard near-black fragments (maxRGB
< 0.1) since they contribute nothing visually but render as dark
rectangles against sky/terrain.
2. Expand colorKeyBlack texture keyword detection to include "lamp",
"lantern", "glow", "flare", "brazier", "campfire", "bonfire" in
addition to the existing "candle", "flame", "fire", "torch".
3. Expand flameLikeModel detection for glow sprite conversion to include
"brazier", "campfire", "bonfire". Also compute glow centers for
colorKeyBlack batches (not just blendMode >= 3) so glow sprites
position correctly for all flame-like objects.