- WCP format: simple binary archive with magic header, JSON manifest,
and concatenated file data. Not tied to any proprietary format.
- ContentPacker::packZone(): bundles all zone files from output dir
into a single .wcp file (terrain, objects, creatures, quests, manifest)
- ContentPacker::unpackZone(): extracts .wcp to a directory
- ContentPacker::readInfo(): reads pack metadata without extracting
- Format: "WCP1" magic + fileCount + infoJSON + file table + data
- Foundation for distributing custom zones to other wowee users
and private servers
Note: currently bundles ADT/WDT files as-is. Future: convert terrain
to open format (heightmap + JSON) for fully open redistribution.
- "Create + Generate" was setting a flag but never calling generateCompleteZone()
- Now properly chains: create terrain → run full procedural pipeline
(noise → smooth → normals → height paint → slope paint → detail → water → beaches)
- Uses generateAfterCreate_ flag to defer generation until after terrain is created
- Pick height, Punch Hole, Fill Hole, Delete All in Radius buttons
no longer require cursor to be actively on terrain
- All buttons now use last known brush position consistently
- Fixes the fundamental UX issue where clicking a UI button moves
the cursor off terrain, making the button do nothing
- About dialog was broken because ImGui::OpenPopup inside BeginMenu
has wrong ID scope — popup never appeared
- Changed to a proper ImGui::Begin window with showAbout_ flag
- Closeable with X button or clicking About again
- Shows version, author, feature summary, and tech stack
- Removed isActive() requirement from all "Create at Cursor" buttons
(Mesa, Crater, Hill, Valley, Stamp, Ridge, Flatten Platform, Holes)
- Buttons now use the last known brush position which persists when
cursor moves to the UI panel to click the button
- Fixes: "Create Mesa at Cursor can't work if cursor is clicking
that button" — now it uses wherever the cursor was last on terrain
- Set Start/End buttons for river/road no longer require brush to be
actively on terrain at click time — uses last known brush position
which persists when cursor moves to the UI panel
- Shows cursor coordinates above the buttons for visual confirmation
- Edge ramp confirmed to only affect terrain heights, not water levels
(water has its own height independent of terrain)
- Ghost model state (ID, path, active flag) properly reset when
clearObjects() wipes the M2 renderer — prevents stale ghost
references causing crashes on subsequent hover
- Ghost model ID uses 59999 to avoid collision with placed object IDs
- Ghost loadModel failure now handled gracefully (returns early)
- Ctrl+Shift+Z redo now works globally (was only in Sculpt mode)
- Ctrl+Z undo works in all modes with toast confirmation
- centerOnTerrain() uses actual mesh chunk positions instead of formula
- Toast shows "Undo" / "Redo" / "Undo placement" for user feedback
- Camera now positions at the center of actual loaded terrain chunks
instead of using formula-based coordinates that can be wrong for
instanced maps
- Falls back to formula if no valid chunks loaded
- Fixes camera starting far from terrain on some maps
- ADT doodad/WMO positions now converted from ADT space to render coords
via core::coords::adtToWorld() — fixes objects appearing at wrong positions
when loading existing WoW maps
- Selecting a map in the Load dialog now auto-finds the first valid tile
(no more clicking "Find Tile" manually — it's automatic)
- Better error messages with toast for failed terrain loads
- Validates mesh has valid chunks before attempting GPU upload
Root cause of ADT loading failures: instanced maps (dungeons, raids)
have arbitrary internal coord values (e.g. tile 62,0 for a file named
_28_27.adt). The terrain mesh generator uses these coords to compute
world positions, resulting in chunks placed thousands of units away
from the camera → "Failed to upload terrain to GPU".
Fix: override terrain_.coord with the tileX/tileY from the filename
before mesh generation. This ensures chunks are positioned correctly
regardless of what the ADT file's internal header says.
- Quick Biome Paint: select a biome (Grassland/Forest/Desert/Snow/Swamp/Barrens)
and apply its full texture set in one click
- Sets base texture for all chunks + scatters variation patches
- Each biome has a primary ground texture + secondary variation
- Much faster than manual painting for initial zone texturing
- Scatter Patches: randomly places texture circles across the terrain
for natural-looking surface variation (dirt patches, rock outcrops)
- Configurable count, min/max radius, and seed
- Preset buttons for dirt and rock patches
- Uses the existing paint() method with random positions
- Adds visual variety to otherwise uniform terrain texturing
- Rotate 90 CW button in Mirror/Rotate section
- Snapshots outer vertex heights into 129x129 grid, rotates in-place,
writes back with inner vertices averaged from surrounding outers
- Useful for reorienting terrain features or creating rotational
symmetry (rotate + mirror for 4-way symmetric arenas)
- Gradient Blend: smoothly transitions base texture from one biome
to another across the entire tile (horizontal or vertical)
- Preset buttons: Grass→Sand and Grass→Snow gradients
- Creates natural biome transition zones without manual painting
- Alpha blending from 0% to 100% across 16 chunks
- Detail Noise: adds high-frequency roughness to existing terrain
without destroying the overall shape (amplitude 0.5-10, freq 0.01-0.5)
- Useful after smooth/generate to break up unnaturally smooth surfaces
- Workflow: generate → smooth → detail noise for natural look
- Separate seed from main noise generator for independent control
- paintAlongPath(): paints a texture along a line segment with
configurable width and quadratic edge falloff
- Road mode now automatically applies Elwynn cobblestone texture
when flattening a road path (flatten + texture in one operation)
- Quick distance check skips chunks far from the path for performance
- Alpha blending uses max() so overlapping paths don't wash out
- Edge Ramp: smoothly transitions tile borders to a target height
so adjacent tiles can connect seamlessly
- Configurable target height and ramp width (how far in from the edge)
- Quadratic blend for smooth start from edge → interior
- Essential for multi-tile zone creation: ramp each tile's edges to
match its neighbor's border height
- Smooth Beaches button in Water panel: creates gentle beach slopes
near water level by blending terrain toward waterline height
- Configurable beach width (15 units default)
- Quadratic falloff creates natural concave beach profile
- Use after filling water to soften harsh land-water edges
- Workflow: sculpt → fill water → smooth beaches → paint sand
- Voronoi noise generator: creates cell-like terrain patterns with
ridge features at cell boundaries (F2-F1 distance field)
- Configurable cell count (5-100) and amplitude
- Toggle between Value noise and Voronoi in the noise generator section
- Creates interesting mesa/plateau formations and organic shapes
- Random cell centers with per-cell height variation
- Offset Heights: shifts all terrain heights by a constant amount
(-100 to +100 range with Apply button)
- Useful for raising terrain above water level or sinking below
- Slider + Apply button in the Noise Generator section
- Invert Heights: flips terrain upside-down around midpoint (mountains
become valleys and vice versa)
- Fill Entire Tile with Water: one-click fills all 256 chunks at the
configured water height and liquid type
- Remove ALL Water: clears water from every chunk instantly
- Water panel now has three water operations: fill tile, remove under
brush, remove all
- fillWater() and invertHeights() methods on TerrainEditor