- Add magma/slime rendering path to water shader (fbm noise, crust/molten/core coloring)
- Fix WMO liquid height filter rejecting high-altitude zones like Ironforge (Z>300)
- Allow interior WMO magma/slime MLIQ groups to load (skip only water/ocean)
- Mark LAVASTEAM.m2 as spell effect for proper additive blend, hide emission mesh
- Add isLavaModel flag for M2 ForgeLava/LavaPots UV scroll fallback
- Add isLava material detection in WMO renderer for lava texture UV animation
- Fix WMO material UBO colors for magma (was blue, now orange-red)
- Add case-insensitive "glass" detection for WMO window materials
- Make instance (WMO-only) glass highly transparent (12-35% alpha)
so underwater scenes are visible through Deeprun Tram windows
- Keep normal world windows at existing opacity (40-95% alpha)
- Disable shadow mapping for interior WMO groups to fix dark
indoor areas like Ironforge
- Stronger wall collision push (0.35/0.15) and swept push (0.45/0.25)
for interior/exterior WMOs to reduce clipping through tunnel walls
- Use all triangles (not just pre-classified walls) for collision checks
- Allow invisible collidable triangles (MOPY 0x01 without 0x20) to block
- Pass insideWMO flag to all collision callers, match swim sweep to ground
- Widen swept hit detection radius from 0.15 to 0.25
- Restrict camera zoom to 12 units inside WMO interiors
- Fix /unstuck launching player above WMOs: remove +20 fallback, use
gravity when no floor found
- Slash and Enter keys always focus chat unless already typing
WMO origins can be far from their visible geometry, causing large city
buildings to be culled from the shadow pass. Use world bounding box for
instance culling and per-group AABB culling. Also increase WMO shadow
cull radius to match the shadow map coverage (180 units).
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).
Parse MOPY per-triangle flags in WMO groups and exclude detail/decorative
triangles (flag 0x04) from collision detection. This prevents invisible
walls from objects like gears and railings in WMO interiors.
Add WotLK area trigger IDs 2173/2175 to extended-range tram triggers.
- Fix Stormwind barracks floor: interior WMO groups named "facade" were
incorrectly marked as LOD shells and hidden when close. Add !isIndoor
guard to all LOD detection conditions so interior groups always render.
- Fix water exit stair clipping: anchor lastGroundZ to current position
on swim exit, set grounded=true for full step-up budget, add upward
velocity boost to clear stair lip geometry.
- Re-enable NPC humanoid equipment geosets (kEnableNpcHumanoidOverrides)
so guards render with proper armor instead of underwear.
- Keep instance portal GameObjects animated (spinning/glowing) instead
of freezing all GO animations indiscriminately.
- Fix equipment disappearing after instance round-trip by resetting
dirty tracking on world reload.
- Fix multi-doodad-set loading: load both set 0 (global) and placement-
specific doodad set, with dedup to avoid double-loading.
- Clear placedWmoIds in softReset/unloadAll to prevent stale dedup.
- Apply MODF rotation to instance WMOs, snap player to WMO floor.
- Re-enable rebuildSpatialIndex in setInstanceTransform.
- Store precomputeFloorCache results in precomputed grid.
- Add F8 debug key for WMO floor diagnostics at player position.
- Expand mapIdToName with all Classic/TBC/WotLK instance map IDs.
Low-vertex groups (<100 verts) were incorrectly marked as distance-only
LOD shells in small WMOs like Stockades. Now only applies this heuristic
to large WMOs (50+ groups) where it's needed for city exterior shells.
Add null checks for vertex/index buffers, pipelines, and zero-count
draws in WMO render path. The shadow pass already had buffer validation
but the main render() was missing it, which could cause GPU crashes
on WMO-only maps like Stockades (26 groups).
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.
- Fix WDT chunk magic constants to big-endian ASCII (matching ADTLoader)
- Add minimum effective size for box area triggers (90 units, like sphere 45-unit radius)
- Add areaTriggerSuppressFirst_ flag to prevent portal ping-pong on map transfer
- Add WMORenderer::clearAll() to clear models/textures on map change (prevents GPU crash)
- Increase WMO texture cache default to 8GB
- Fix setMapName called after loadTestTerrain so WMO renderer exists
- Save/restore player position around CMSG_AREATRIGGER to prevent bad DB persistence
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.
- 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
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.
Instead of relying on material flag 0x40 (F_CLAMP_S, not F_WINDOW) to
identify glass materials, detect windows by checking for "window" in the
texture path. This correctly applies glass to window textures while
leaving lamp posts and other geometry opaque.
Two WMO rendering fixes:
1. Glass batch merging: BatchKey didn't include isWindow, so window and
non-window batches sharing the same texture got merged together. If
the window batch was processed first, the entire merged batch (lamp
base, iron frame, etc.) rendered as transparent glass. Add isWindow
to the batch key so glass/non-glass batches stay separate.
2. LOD group culling: WMO groups named with "LOD" are distance-only
impostor geometry (e.g. cathedral tower extension, hill tower). They
should only render beyond 200 units. Store raw MOGN chunk for
offset-based name lookup, detect "lod" in group names during load,
and skip LOD groups in both main and shadow passes when camera is
within range.
F_SIDN (0x20) is the night-glow/self-illuminated flag, not a window
flag. Only F_WINDOW (0x40) should trigger transparent glass rendering.
The previous mask (0x60) caught both flags, making lamp post bases,
iron frames, and other SIDN-flagged surfaces render as transparent
glass with Fresnel reflections instead of opaque materials.
POM fixes: use blurred height only for ray march (keep crisp Sobel for
normals), reduce pomScale 0.03→0.012, clamp grazing angle denominator
to 0.15, hard-limit max UV offset, smooth fadeout at steep view angles.
Add Normal Map Strength slider (0.0-2.0) in Video settings for user
control over surface detail intensity. Persisted across sessions.
Generate normal+height maps from diffuse textures at load time using
luminance-to-height and Sobel 3x3 filtering. Compute per-vertex tangents
via Lengyel's method for TBN basis construction.
Fragment shader uses screen-space UV derivatives (dFdx/dFdy) for smooth
LOD crossfade and angle-adaptive POM sample counts. Flat textures
naturally produce low height variance, causing POM to self-select off.
Settings: Normal Mapping on by default, POM off by default with
Low/Medium/High quality presets. Persisted across sessions.
Dedicated Vulkan pipeline with alpha blending AND depth writes so
windows look transparent at oblique angles without see-through artifacts.
Fresnel alpha ranges from 0.4 (grazing) to 0.95 (head-on) with sun
glint, reflections, and silver-lining specular.
- 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%
- SHADOW_MAP_SIZE 2048→1024: 4x fewer pixels rasterized in depth pass
- Replace 9-tap manual PCF loop with single hardware PCF tap in all 4 receiver
shaders (terrain.frag, wmo_renderer, m2_renderer, character_renderer).
GL_LINEAR + GL_COMPARE_REF_TO_TEXTURE already gives 2×2 bilinear PCF per
tap for free, so quality is maintained while doing 9x fewer texture fetches.
- Throttle shadow depth pass to every 2 frames; OpenGL depth texture persists
between frames so receivers always have a valid shadow map. 1-frame lag at
60 fps is invisible.
- game_handler.cpp: use-after-move on node.id after std::move(node)
(save nodeId before the move)
- tcp_socket.cpp, world_socket.cpp: virtual call in destructor bypasses
dispatch; use qualified TCPSocket::disconnect() / WorldSocket::disconnect()
to make intent explicit
- wmo_renderer.cpp: float loop counters risk precision drift; replace with
integer step counts and reconstruct float from index
- game_screen.cpp: (float + 0.5) cast to int is incorrect rounding;
use std::lround instead
M2 models like OrgrimmarFloatingEmbers and OrgrimmarSmokeEmitter have a
simple box mesh (24 verts, 36 indices) meant only to define particle
emitter bounds. Their blendMode was 0 (opaque), causing them to render
as large grey boxes. Detect these by checking for box geometry with
particle emitters and large bounds (>5 units), then mark as invisible.
Also add ANTIPORTAL and batch-disable flag checks to WMO group filtering.
- Skip WMO batches with material flags F_SIDN (0x20) or F_WINDOW (0x40)
which are transparent sky/window panes that render as grey curtains
- Fix /unstuck: always nudge 5 units forward first, then sample floor at
destination instead of teleporting back to last safe position (which
could be the stuck location itself)
The previous fix only skipped groups where ALL batches were untextured.
Groups with a mix of textured and untextured batches still rendered the
untextured ones as solid grey geometry. Now skip each untextured batch
individually during rendering.
- Per-family mount sounds (kodo, tallstrider, mechanostrider, etc.) detected from M2 model path
- Skip WMO groups with SHOW_SKYBOX flag or all-untextured batches (grey mesh in Orgrimmar)
- Freeze physics during taxi landing until terrain loads to prevent falling through void
- Disable bone animations on tropical vegetation (palm, bamboo, banana, etc.) to fix wiggling
- Snap player to final taxi waypoint on flight completion
- Extract mount aura spell ID from classic UNIT_FIELD_AURAS for CMSG_CANCEL_AURA dismount
- Increase /unstuck forward nudge to 5 units