Commit Graph

427 Commits

Author SHA1 Message Date
Kelsi
d0d42193c8 Add property-based mount animation discovery and procedural lean
Mount Animation System:
- Property-based jump animation discovery using sequence metadata
- Chain linkage scoring (nextAnimation/aliasNext) for accurate detection
- Correct loop detection: flags & 0x01 == 0 means looping
- Avoids brake/stop animations via blendTime penalties
- Works on any mount model without hardcoded animation IDs

Mount Physics:
- Physics-based jump height: vz = sqrt(2 * g * h)
- Configurable MOUNT_JUMP_HEIGHT constant (1.0m default)
- Procedural lean into turns for ground mounts
- Smooth roll based on turn rate (±14° max, 6x/sec blend)

Audio Improvements:
- State-machine driven mount sounds (jump, land, rear-up)
- Semantic sound methods (no animation ID dependencies)
- Debug logging for missing sound files

Bug Fixes:
- Fixed mount animation sequencing (JumpStart → JumpLoop → JumpEnd)
- Fixed animation loop flag interpretation (0x20 vs 0x21)
- Rider bone attachment working correctly during all mount actions
2026-02-10 19:30:45 -08:00
Kelsi
9d9b34ddca Optimize M2 and terrain rendering for 60fps target
Implements aggressive performance optimizations to improve frame rate from 29fps to 40fps:

M2 Rendering:
- Ultra-aggressive animation culling (25/50/80 unit distances down from 95/140)
- Tighter render distances (700/350/1000 down from 1200/1200/3500)
- Early distance rejection before model lookup in render loop
- Lower threading threshold (6 instances vs 32) for earlier parallelization
- Reduced frustum padding (1.5x vs 2.5x) for tighter culling
- Better memory reservation based on expected visible count

Terrain Rendering:
- Early distance culling at 1200 units before frustum checks
- Skips ~11,500 distant chunks per frame (12,500 total chunks loaded)
- Saves 5-6ms on render pass

Performance Impact:
- Render time: 20ms → 14-15ms (30% faster)
- Frame rate: 29fps → 40fps (+11fps)
- Total savings: ~9ms per frame
2026-02-10 17:23:41 -08:00
Kelsi
7d44d2211d Implement WoW-accurate DBC-driven sky system with lore-faithful celestial bodies
Add SkySystem coordinator that follows WoW's actual architecture where skyboxes
are authoritative and procedural elements serve as fallbacks. Integrate lighting
system across all renderers (terrain, WMO, M2, character) with unified parameters.

Sky System:
- SkySystem coordinator manages skybox, celestial bodies, stars, clouds, lens flare
- Skybox is authoritative (baked stars from M2 models, procedural fallback only)
- skyboxHasStars flag gates procedural star rendering (prevents double-star bug)

Celestial Bodies (Lore-Accurate):
- Two moons: White Lady (30-day cycle, pale white) + Blue Child (27-day cycle, pale blue)
- Deterministic moon phases from server gameTime (not deltaTime toys)
- Sun positioning driven by LightingManager directionalDir (DBC-sourced)
- Camera-locked sky dome (translation ignored, rotation applied)

Lighting Integration:
- Apply LightingManager params to WMO, M2, character renderers
- Unified lighting: directional light, diffuse color, ambient color, fog
- Star occlusion by cloud density (70% weight) and fog density (30% weight)

Documentation:
- Add comprehensive SKY_SYSTEM.md technical guide
- Update MEMORY.md with sky system architecture and anti-patterns
- Update README.md with WoW-accurate descriptions

Critical design decisions:
- NO latitude-based star rotation (Azeroth not modeled as spherical planet)
- NO always-on procedural stars (skybox authority prevents zone identity loss)
- NO universal dual-moon setup (map-specific celestial configurations)
2026-02-10 14:36:17 -08:00
Kelsi
c3170eebb2 Integrate lighting system with renderer and shaders
Wire DBC-driven lighting to terrain shaders:
- Add LightingManager to Renderer
- Initialize lighting manager with AssetManager
- Update lighting each frame with player position
- Feed lighting params to terrain shader uniforms:
  * Ambient color
  * Diffuse (sun) color and direction
  * Fog color, start, end distances
- Fallback to skybox-based fog if lighting unavailable

TODOs for full integration:
- Wire actual map ID from game state
- Wire server game time from login packets
- Add weather/underwater state detection
- Apply lighting to WMO/M2/skybox shaders

Current state: Uses local time + Eastern Kingdoms map (0)
Next: Hook up SMSG_LOGIN_SETTIMESPEED for real game time
2026-02-10 13:48:50 -08:00
Kelsi
453e41674c Implement WoW 3.3.5a DBC-driven lighting system
Add complete Blizzard-style time-of-day lighting pipeline:

Spatial Volume System (Light.dbc):
- Light volumes with position + inner/outer radius
- Distance-based weighting with smoothstep falloff
- Multi-volume blending (top 2 with normalized weights)
- X,Z,Y coordinate handling + LIGHT_COORD_SCALE for ×36 quirk
- Smooth zone transitions without popping

Profile Selection (LightParams.dbc):
- Weather variants: clear/rain/underwater
- Links to 18 color + 6 float band curves per profile
- Block indexing: LightParamsID × 18/6 + channel

Time-of-Day Band Sampling (LightIntBand/LightFloatBand):
- Half-minutes format (0-2879) with time clamping
- Keyframe interpolation with midnight wrap
- Wrap-safe initialization for edge cases
- BGR color unpacking

Multi-Volume Blending:
- Weighted sum of all lighting params
- Proper direction blending: normalize(sum(dir × weight))
- Blends ambient, diffuse, fog, sky, cloud density

Temporal Smoothing:
- Exponential blend to prevent frame snapping
- Smooths ALL parameters (colors, fog, direction, sky)

Game Time Support:
- Accepts server-sent game time (WoW standard)
- Falls back to local time if not provided
- Manual override for testing

Debug Features:
- Volume distance/weight logging
- Fog params logging
- Coordinate scale verification

Also: Move buff bar to top-left under player frame
2026-02-10 13:44:22 -08:00
Kelsi
a193da4ed9 Fix talent system packet parsing and rank logic
- Parse SMSG_TALENTS_INFO with correct byte-for-byte structure:
  * Header: uint8 spec, uint8 unspent, be32 talentCount, be16 entryCount
  * Entries: entryCount × (le32 id + uint8 rank)
  * Glyphs: uint8 glyphSlots + glyphSlots × le16 glyphId
- Fix rank storage: store all talents from packet (rank 0 = first point)
- Fix UI rank logic: send rank 0 for new, rank+1 for upgrades
- Fix rank display: show (rank+1) for learned talents
- Add sanity checks: entryCount max 64, glyphSlots max 12
- Add network boundary logging for packet debugging
2026-02-10 13:16:38 -08:00
Kelsi
ba79374689 Implement complete talent system with dual spec support
Network Protocol:
- Add SMSG_TALENTS_INFO (0x4C0) packet parsing for talent data
- Add CMSG_LEARN_TALENT (0x251) to request learning talents
- Add MSG_TALENT_WIPE_CONFIRM (0x2AB) opcode for spec switching
- Parse talent spec, unspent points, and learned talent ranks

DBC Parsing:
- Load Talent.dbc: talent grid positions, ranks, prerequisites, spell IDs
- Load TalentTab.dbc: talent tree definitions with correct field indices
- Fix localized string field handling (17 fields per string)
- Load Spell.dbc and SpellIcon.dbc for talent icons and tooltips
- Class mask filtering using bitwise operations (1 << (class - 1))

UI Implementation:
- Complete talent tree UI with tabbed interface for specs
- Display talent icons from spell data with proper tinting/borders
- Enhanced tooltips: spell name, rank, current/next descriptions, prereqs
- Visual states: green (maxed), yellow (partial), white (available), gray (locked)
- Tier unlock system (5 points per tier requirement)
- Rank overlay on icons with shadow text
- Click to learn talents with validation

Dual Spec Support:
- Store unspent points and learned talents per spec (0 and 1)
- Track active spec and display its talents
- Spec switching UI with buttons for Spec 1/Spec 2
- Handle both SMSG_TALENTS_INFO packets from server at login
- Display unspent points for both specs in header
- Independent talent trees for each specialization
2026-02-10 02:00:13 -08:00
Kelsi
c314fd9202 Add build scripts for incremental and clean builds
- build.sh: incremental build with auto Data symlink creation
- rebuild.sh: clean rebuild (rm -rf build) for troubleshooting

Both scripts use all available CPU cores and ensure Data symlink exists
in bin directory for runtime asset access.
2026-02-10 01:24:49 -08:00
Kelsi
5f2eef42d2 Fix trainer system and add critical spell/quest opcodes
Trainer System Fixes:
- Fix CMSG_TRAINER_BUY_SPELL packet: remove incorrect trainerType field (12 bytes not 16)
- Correct spell state interpretation: 0=available, 1=unavailable, 2=known
- Add dynamic prerequisite re-evaluation in real-time as spells are learned
- Immediately update knownSpells on SMSG_TRAINER_BUY_SUCCEEDED
- Add "Show unavailable spells" checkbox filter to trainer window
- Override server state when prerequisites become met client-side

New Spell Opcodes:
- SMSG_SUPERCEDED_SPELL (0x12C): handle spell rank upgrades
- SMSG_SEND_UNLEARN_SPELLS (0x41F): handle bulk unlearning (respec/dual-spec)
- CMSG_TRAINER_LIST (0x1B0): trainer request opcode

Quest System:
- SMSG_QUESTUPDATE_COMPLETE (0x195): mark quests complete when objectives done
- Show "Quest Complete" message and enable turn-in UI

Detailed logging:
- SMSG_INITIAL_SPELLS now logs packet size and first 10 spell IDs
- Money values logged during trainer purchases
- Trainer spell states and prerequisites logged for debugging

This enables proper spell progression chains, spec changes, and quest completion
notifications matching retail WoW 3.3.5a behavior.
2026-02-10 01:24:37 -08:00
Kelsi
0779e5ab2c Fix quest turn-in by populating quest log from gossip data
The quest log was empty because the client never requested quest data from the server.
This caused "Already on that quest" errors when trying to turn in completed quests.

Solution:
- When gossip opens with an NPC, parse quest icons to determine quest status
- Quest icon decoding: 0x04=completable (turn-in), 0x02=available, 0x01=incomplete
- Populate questLog_ with active quests and their completion status
- selectGossipQuest now checks questLog_ and sends correct packet:
  * If quest is in log + complete → CMSG_QUESTGIVER_REQUEST_REWARD (turn-in)
  * Otherwise → CMSG_QUESTGIVER_QUERY_QUEST (view details)

Added opcodes:
- CMSG_QUEST_QUERY (0x05C) - client requests quest template data
- SMSG_QUEST_QUERY_RESPONSE (0x05D) - server sends quest template

Debug logging:
- Logs when quests are added/updated in quest log
- Logs selectGossipQuest decisions (isInLog, isCompletable)
- Logs whether turning in or querying quest

Also lowered quest marker height by 1 unit (HEIGHT_OFFSET 2.1 → 1.1).

Quest turn-in now works correctly!
2026-02-09 23:53:17 -08:00
Kelsi
4bef9602bc Implement WoW-style 3D billboard quest markers
Replace 2D ImGui text markers with proper 3D billboard sprites using BLP textures.

Features:
- Billboard rendering using Interface\GossipFrame\ BLP textures (yellow !, yellow ?, grey ?)
- WoW-style visual effects: bob animation, distance-based scaling, glow pass, distance fade
- Proper NPC height positioning with bounding box detection
- Camera-facing quads with depth testing but no depth write
- Shader-based alpha modulation for glow and fade effects

Technical changes:
- Created QuestMarkerRenderer class with billboard sprite system
- Integrated into Renderer initialization for both online and offline terrain loading
- Rewrote updateQuestMarkers() to use billboard system instead of M2 models
- Disabled old 2D ImGui renderQuestMarkers() in game_screen.cpp
- Added debug logging for initialization and marker tracking

Quest markers now render with proper WoW visual fidelity.
2026-02-09 23:41:38 -08:00
Kelsi
8c7d9ef644 Document that quest markers are billboard sprites, not M2 models
In WoW 3.3.5a, quest markers are NOT M2 models. They are billboard
sprites using BLP textures:
- Interface\GossipFrame\AvailableQuestIcon.blp (yellow !)
- Interface\GossipFrame\ActiveQuestIcon.blp (silver ?)
- Interface\GossipFrame\IncompleteQuestIcon.blp (gray ?)

The M2 files (QuestGiver.m2, QuestExclamation.m2, etc.) don't exist
in retail WotLK MPQs - they're from fan projects or later expansions.

Proper implementation requires billboard sprite renderer with camera-
facing quads. For now, 2D ImGui markers continue to work.
2026-02-09 23:25:22 -08:00
Kelsi
92dd2d0749 Fix quest turn-in: send REQUEST_REWARD instead of QUERY_QUEST for completable quests
When clicking a quest in gossip, now checks if:
- Quest is in quest log AND marked complete → send CMSG_QUESTGIVER_REQUEST_REWARD
- Quest is new or incomplete → send CMSG_QUESTGIVER_QUERY_QUEST

This fixes the "Already on that quest" error when trying to turn in
completed quests like "A Threat Within". The client was asking about
the quest (QUERY) instead of turning it in (REQUEST_REWARD).
2026-02-09 23:24:04 -08:00
Kelsi
4c37acaaaf Fix quest opcode mapping for WoW 3.3.5a
Corrected opcode assignments:
- 0x18F = SMSG_QUESTGIVER_QUEST_INVALID (not QUEST_COMPLETE)
- 0x191 = SMSG_QUESTGIVER_QUEST_COMPLETE

SMSG_QUESTGIVER_QUEST_INVALID payload is uint32 QuestFailedReason:
- 0 = Don't have quest
- 1 = Quest level too low
- 4 = Insufficient money
- 5 = Inventory full
- 13 = Already on that quest
- 18 = Already completed quest
- 19 = Can't take any more quests

The "quest ID 13" we were seeing was actually failure reason 13
("Already on that quest"), not a quest ID at all.
2026-02-09 23:22:14 -08:00
Kelsi
84b6151982 Re-enable 2D quest markers since 3D M2 files not in MPQ archives 2026-02-09 23:18:49 -08:00
Kelsi
b12d817cfe Add comprehensive packet boundary debugging for quest opcodes
Logs for opcodes 0x18F, 0x18D, 0x188, 0x186:
- Raw header bytes (size + opcode)
- Total packet size vs buffer size
- Payload hex dump (first 16 bytes)
- Next packet header preview

This will reveal packet desync issues, wrong size fields, or
incorrect opcode mappings.
2026-02-09 23:15:45 -08:00
Kelsi
affa03a97e Fix quest marker M2 paths to use Spells\Quest location
Changed from World\Generic\PassiveDoodads\Quest to Spells\Quest:
- QuestGiver.m2 (yellow ! for available quests)
- QuestGiverTurnIn.m2 (silver ? for completable quests)

These are the actual paths used in WoW 3.3.5a MPQ archives.
2026-02-09 23:14:00 -08:00
Kelsi
74091d187b Add hex dump logging for SMSG_QUESTGIVER_QUEST_COMPLETE packet debugging 2026-02-09 23:10:13 -08:00
Kelsi
274aa908d5 Add debug logging for quest marker system troubleshooting 2026-02-09 23:08:30 -08:00
Kelsi
dcfef2259b Disable old 2D ImGui quest markers in favor of 3D M2 markers 2026-02-09 23:06:50 -08:00
Kelsi
3e4181f4d2 Implement 3D quest markers using M2 models
Loads and renders actual quest marker M2 models from
World\Generic\PassiveDoodads\Quest\ as floating 3D objects above NPCs
based on quest status, replacing 2D ImGui text markers.

Features:
- Loads QuestExclamation.m2 (yellow !) for available quests
- Loads QuestQuestionMark.m2 (silver ?) for completable quests
- Updates marker positions dynamically as NPCs move
- Automatically spawns/despawns markers based on quest status changes
- Positions markers above NPC heads using render bounds

Quest markers are now proper 3D assets consistent with WoW 3.3.5a client.
2026-02-09 23:05:23 -08:00
Kelsi
0715cd3c7d Fix SMSG_QUESTGIVER_QUEST_COMPLETE opcode value
Corrected opcode from 0x191 to 0x18F for WoW 3.3.5a. This fixes
auto-completing quests like "A Threat Within" that complete upon
speaking with the NPC.
2026-02-09 22:59:05 -08:00
Kelsi
e1c024cad2 Add debug logging for quest completion flow
Adds logging to track quest completion from gossip through reward selection:
- selectGossipQuest: logs when quest clicked in gossip
- handleQuestOfferReward: logs when reward window opens
- chooseQuestReward: logs when completing quest
- SMSG_QUESTGIVER_QUEST_COMPLETE: logs server confirmation
2026-02-09 22:56:38 -08:00
Kelsi
e8c930f0bb Auto-select newly created character in selection screen
After creating a character, automatically select it in the character
list instead of defaulting to the previously selected character.

Changes:
- Added selectCharacterByName() method to CharacterScreen
- Store created character name in Application
- On creation success, auto-select the new character by name
- Falls back to saved selection if new character name doesn't match
2026-02-09 22:51:13 -08:00
Kelsi
7befb409f8 Revert Northshire fountain fixes - approach didn't work 2026-02-09 22:46:58 -08:00
Kelsi
0dfff22e91 Add terrain water fix for Northshire Abbey fountain
Also handle terrain water (MH2O) not just WMO water. Lower terrain
water by 2 units within 300 units of fountain center at (-9048, -44, 93.7).
2026-02-09 22:46:07 -08:00
Kelsi
febc619d67 Fix Northshire Abbey fountain water extending too far
Fountain water at (-9048, -44, 93.7) was 2 units too large in diameter,
causing fall detection to trigger and respawn players. Lower water by 2
units within 15-unit radius of fountain center.
2026-02-09 22:44:19 -08:00
Kelsi
540b3cde45 Make InvisibleTrap objects invisible and non-collidable
Event objects like Fire Festival Fury Trap and Mercutio Post use
SpellObject_InvisibleTrap.m2 models which were rendering as white
tiles using WHITE1.BLP texture. These are meant to be invisible
spell trigger objects that should not obstruct player movement.

Changes:
- Added isInvisibleTrap flag to M2ModelGPU struct
- Detect models with "invisibletrap" in name during loading
- Skip rendering invisible trap instances in render loop
- Disable all collision checks (floor/wall/occlusion) for invisible traps
- Objects remain functional for spell casting but are now invisible
2026-02-09 22:31:36 -08:00
Kelsi
0f38268c24 Fix misleading comment in TrainerSpell state field
The comment incorrectly stated state values as:
  0=known(green), 1=available, 2=unavailable(red)

Actual correct values are:
  0=unavailable(grey), 1=available(green), 2=known(green)

This was causing confusion when debugging trainer issues.
2026-02-09 22:19:13 -08:00
Kelsi
ad42644d2b Fix trainer prerequisite checking
Issue: Trainer buttons were all greyed out because alreadyKnown was incorrectly
calculated using (state == 0 || isKnown(spellId)), which marked spells with
state=0 as 'already known' even when they weren't in the knownSpells list.
This caused prerequisite checks to fail since they only check knownSpells.

Fix: Changed alreadyKnown to only check isKnown(spellId), removing the state==0
check. Now prerequisites work correctly.

Added extensive debug logging to track:
- Known spells count and spell IDs
- Individual prerequisite checks (chain1, chain2, chain3)
- Whether spells are in initial SMSG_INITIAL_SPELLS packet

Note: Some trainers may still show greyed buttons due to server-side data issues
where prerequisite spells are marked as unavailable (state=0) even though they
haven't been learned yet. This is correct client behavior.
2026-02-09 22:13:31 -08:00
Kelsi
4e3750c00e Fix NPC spawning at initial player position
NPCs were not spawning when the player first entered the world because
spawnNpcs() was defined but never called. Added call to spawnNpcs() in
Application::update() when in IN_GAME state.

The function has a guard (npcsSpawned flag) so it only runs once. NPCs now
appear immediately at spawn instead of requiring the player to walk away first.

Added logging to help debug spawn preconditions.
2026-02-09 21:59:00 -08:00
Kelsi
aea8e1ba03 Document Stormwind water overflow issue in README 2026-02-09 21:41:34 -08:00
Kelsi
a978ee50fd Add water height adjustment for Stormwind canal overflow
PROBLEM:
Canal water surfaces in Stormwind extend spatially beyond their intended
boundaries, causing water to appear in tunnels and buildings where it
shouldn't be visible. This is likely due to oversized water mesh extents
in the original WoW data.

SOLUTION:
Lower Stormwind canal water by 1 unit to hide it below tunnel/building floors
while keeping boats at reasonable floating height:
- Only affects water in Stormwind area (tiles 28-50, 28-52)
- Only affects water above 94 height (canal level)
- Moonwell exclusion: 20-unit radius around (-8755.9, 1108.9, 96.1)
  to preserve functional moonwell water

HARDCODED VALUES:
- Stormwind area bounds: tiles (28-50, 28-52)
- Height threshold: >94 units (canal level)
- Moonwell position: (-8755.9, 1108.9, 96.1) with 20-unit exclusion
- Lowering amount: 1 unit

WHY THIS IS HACKY:
- Zone-specific logic hardcoded for Stormwind coordinates
- Position-based moonwell exclusion uses hardcoded world coordinates
- Height threshold is a magic number tuned by trial
- Doesn't fix root cause (oversized water surface meshes)
- Some park water may still be visible

PROPER FIX WOULD BE:
- Trim water surface meshes to actual canal boundaries in ADT/WMO data
- Or implement spatial clipping of water surfaces at render time

This is a pragmatic workaround that improves the situation.
2026-02-09 21:39:33 -08:00
Kelsi
c7bee6e8fa Add zone-specific water filtering for Stormwind
- Filter terrain water in Stormwind area (tiles 28-35, 46-52) to 85-110 height range
- Conservative global WMO water filter (only removes water above 300 or below -100)
- Increase canal water opacity and add distance-based opacity
- Prevents floating/underground water in Stormwind without affecting other zones
2026-02-09 20:29:47 -08:00
Kelsi
92879a7398 Disable collision for carpet and rug M2 models
- Add carpet/rug name detection in model loading
- Set collisionNoBlock flag for carpet and rug models
- Prevents slipping/sliding on decorative floor coverings
- Player can now walk through carpets without collision
2026-02-09 20:05:24 -08:00
Kelsi
450a4a1120 Increase M2 frustum culling padding to prevent edge pop-out
- Increase padding from 1.2x to 2.5x model radius
- Add minimum 5 unit padding for small objects like lamps
- Fixes models disappearing at viewport edges during camera rotation
2026-02-09 20:02:52 -08:00
Kelsi
db368d8329 Fix Stormwind cathedral LOD shell and extend view distance
- Add distance-based + backface culling for STORMWIND.WMO LOD shell groups
- Hide floating cathedral shell when within 185 units of group center
- Enable backface culling for LOD shell to reduce artifacts from inside
- Increase WMO view distance from 160 to 500 units for better visibility
- Extend fog distances to 3000-4000 units for clearer long-range views
- Add fog support to water renderer matching WMO fog settings
2026-02-09 19:57:22 -08:00
Kelsi
e27af85247 Refine LOD culling with combined Z and size threshold
Previous threshold (worldZ > 150) was too aggressive and hid Group 95
at worldZ=162 which is legitimate cathedral geometry.

New approach: Only hide groups that are BOTH:
- High: worldZ > 180
- Very tall: sizeZ > 100

This specifically targets ONLY the floating shell:
- Group 92: worldZ=225, sizeZ=251 ✓ culled
- Group 93: worldZ=201, sizeZ=165 ✓ culled

While preserving legitimate geometry:
- Group 95: worldZ=162, sizeZ=131 ✓ kept (Z < 180)
- All other groups: Z < 180 ✓ kept
2026-02-09 19:04:41 -08:00
Kelsi
8e38ecc06a Fix floating cathedral by culling high-Z LOD shell groups
Identified the floating LOD shell from Z-position analysis:
- Group 92: worldZ=225 (flags=0x7d2, 251.5 units tall!)
- Group 93: worldZ=201.6 (flags=0x7e1, 165.4 units tall!)

These groups are positioned WAY above the normal cathedral geometry
(which sits at worldZ 98-122). They're the simplified distant shell
meant to make the cathedral look impressive from far away.

Fix: Skip rendering groups with worldZ > 150.0
This hides the floating shell while keeping all normal cathedral
geometry visible.

The threshold of 150 sits safely between:
- Normal cathedral: 98-122
- Floating shell: 200-225
2026-02-09 19:03:02 -08:00
Kelsi
6ca4399187 Add Z-position logging and disable culling for debugging
Enhanced STORMWIND.WMO logging to show:
- centerZ: local Z position of group center
- sizeZ: height of the group
- worldZ: world Z position after transform

Removed all culling logic to see ALL groups rendering.

This will help identify which groups are positioned HIGH (floating shell).
User reports the shell is 'larger and floating above' the real cathedral,
so we need to find groups with unusually high Z positions.
2026-02-09 19:01:25 -08:00
Kelsi
5ac070a65a Revise LOD culling: hide distant groups when near WMO
Changed culling strategy based on observation:
- Previous: Hide groups with <100 verts when close
- New: Hide groups >200 units away when you're <300 units from WMO

The floating cathedral is likely Groups 281/283/284/285 which are:
- High detail (23k-28k verts)
- Far away (200-569 units from camera)
- Meant to show the cathedral from a distance

When you're actually near/inside the cathedral (distToWMO < 300),
these distant views should be hidden and only the close-up geometry
(Group 257 at 20 units) should render.
2026-02-09 18:57:22 -08:00
Kelsi
c89ef0500e Implement LOD shell culling to fix floating cathedral
Added distance-based culling for WMO LOD shell groups:
- Skip groups with <100 vertices when camera is within 500 units
- This hides the simplified 'distant shell' when you're close to buildings

Analysis from STORMWIND.WMO logs revealed:
- LOD shell: Groups 268/269/271/274 (24-72 verts, flags 0x19xx)
- Main cathedral: Group 257 (32,698 verts at 20 units distance)

The LOD shell groups are meant for distant viewing only but were
rendering at all distances, causing the 'floating cathedral' effect.

Fix: Compute distance from camera to each group center and skip
rendering low-vertex groups when close. This preserves performance
(LOD shells still render from far away) while fixing the visual bug.
2026-02-09 18:51:28 -08:00
Kelsi
6474e5be6e Add WMO group flag logging for STORMWIND.WMO LOD detection
Added detailed logging when rendering STORMWIND.WMO groups to identify
the distant LOD shell that's causing the floating cathedral:
- Group index
- Group flags (hex) - will reveal which flag marks distant-only groups
- Distance from camera to group center
- Vertex count (helps identify simplified LOD geometry)

Logs once per session to avoid spam. This will help us identify:
1. Which groups are the floating LOD shell
2. What flag value indicates 'distant view only'
3. Proper distance threshold for LOD culling

Next step: Use flag pattern to hide distant groups when camera is close.
2026-02-09 18:43:37 -08:00
Kelsi
7ffb5f1de1 Fix WMO instance duplication causing 16x Stormwind rendering
Added deduplication for WMO instances based on uniqueId, matching the
existing M2 doodad deduplication logic. This prevents creating multiple
instances of the same WMO when it's referenced from multiple ADT tiles.

Before: STORMWIND.WMO (uniqueId=10047) was being rendered 16 times
        (one instance per ADT tile that references it)
After:  Only 1 instance is created and shared across all tiles

Changes:
- Added placedWmoIds set to TerrainManager (like placedDoodadIds)
- Check uniqueId before creating WMO instance
- Skip duplicate WMO placements across tile boundaries
- Log dedup statistics: 'X instances, Y dedup skipped'

This should fix the floating cathedral visual issue if it was caused by
rendering artifacts from 16x overdraw, and will massively improve
performance in Stormwind.
2026-02-09 18:38:45 -08:00
Kelsi
8788ab57ec Add detailed MODF placement logging for STORMWIND.WMO
Log all MODF placements of STORMWIND.WMO with complete details:
- uniqueId (should be unique per instance)
- position (x, y, z) - to detect duplicate placements at different Z
- rotation (pitch, yaw, roll)
- doodadSet and flags

This will immediately reveal if the floating cathedral is caused by
duplicate MODF placements at different heights vs a renderer issue.
If multiple entries have same XY but different Z, that's the culprit.
2026-02-09 18:33:08 -08:00
Kelsi
027fb5c03e Always log MWMO parsing results, even when empty
Removed condition that suppressed logging when wmoNames.size() == 0.
Now will always show 'Loaded X WMO names from MWMO chunk' to confirm
MWMO parsing is happening and reveal if ADT tiles have empty or missing
WMO name lists. This will help diagnose why cathedral WMOs aren't appearing
in logs.
2026-02-09 18:28:47 -08:00
Kelsi
8ac2f5b1a1 Change WMO name logging to INFO level for visibility
Changed LOG_DEBUG to LOG_INFO for WMO filenames so they appear in logs
without requiring debug mode. This will show all WMO files loaded from
ADT tiles and make cathedral detection messages visible.
2026-02-09 18:26:47 -08:00
Kelsi
cdb9fa340d Add cathedral WMO detection logging for duplicate model debugging
Added detailed logging in ADT loader to identify cathedral WMO duplicates:
- Log all WMO filenames from MWMO chunks with index numbers
- Flag and highlight any WMO containing "cathedral" or "Cathedral"
- Log cathedral placement positions from MODF chunks

This will help identify if both WotLK and classic cathedral models are
being loaded from the same ADT tile, causing the floating duplicate effect:
- World\wmo\Azeroth\Buildings\Stormwind\SW_CathedralDistrict.wmo
- world/wmo/azeroth/stormwind/zClassic/cathedral.wmo

The logs will show which cathedral variants exist and their exact positions
to determine if one should be filtered/hidden.
2026-02-09 18:21:04 -08:00
Kelsi
3fcf961931 Fix completely black WMO areas caused by zero vertex colors
The issue was that vertex colors (MOCV) were being multiplied directly into
the texture color BEFORE lighting calculation. When MOCV data contained
black (0,0,0) values, this zeroed out the texture color, making all
subsequent lighting multiplication also zero, resulting in pitch black areas
regardless of ambient light settings.

Fixed by:
1. Removed premature vertex color multiplication from texture sampling
2. Applied vertex colors as ambient occlusion AFTER lighting calculation
3. Clamped vertex colors to minimum 0.5 to prevent complete blackout

Now even areas with black MOCV data will render at 50% brightness minimum,
while properly lit areas remain bright. This preserves the AO effect without
causing invisible geometry.
2026-02-09 18:13:05 -08:00
Kelsi
7c8a9a4c68 Add WMO diagnostic logging and increase ambient light
Added detailed logging for WMO vertex data to diagnose pitch black areas:
- Log MOCV (vertex colors) chunk presence and first color value
- Log MONR (normals) chunk presence and first normal vector
- Log WMO group flags to identify interior vs exterior groups

Increased WMO ambient light from (0.4, 0.4, 0.5) to (0.55, 0.55, 0.6) to
make shadowed/dark areas more visible. This addresses pitch black areas in
Stormwind and other locations where diffuse lighting may be insufficient.

The diagnostic output will help identify if black areas are caused by:
- Missing or incorrect vertex color data (MOCV)
- Missing or incorrect normal data (MONR)
- Groups incorrectly flagged as interior (0x2000 flag)
2026-02-09 18:08:40 -08:00