48th procedural mesh: 5-box wooden shipping crate — main
cube body plus 4 reinforcement posts running along the
vertical edges. The posts extend slightly proud of the body
on each axis so they read as separate rails rather than
texture detail, and don't z-fight the body's faces from any
viewing angle.
Useful for dock yards, warehouse interiors, dungeon room
set dressing, NPC merchant shops. Defaults to a 0.80m cube
with 0.05m post half-thickness.
Moves the three item-export handlers (--export-zone-items-md,
--export-project-items-md, --export-project-items-csv) out of
main.cpp into a new cli_items_export.{hpp,cpp} module. All
three render items.json data as human-readable Markdown / CSV
reports for design docs, PR descriptions, GitHub Pages, and
spreadsheet pivot-table workflows.
main.cpp shrinks by 262 lines (5,349 to 5,087).
40th procedural texture: classic gingham picnic-blanket /
shirt fabric — two perpendicular sets of stripes with a
darker color where they cross. The crossing creates the
characteristic 3-tone checker pattern that's distinct from
plain --gen-texture-checker (solid blocks).
Useful for picnic blankets, country tablecloths, NPC shirt
textures, fabric set dressing. Defaults to 16-px stripe
spacing with 8-px stripe width (50% coverage per axis).
Moves the four random-population handlers (--random-populate-zone,
--random-populate-items, --gen-random-zone, --gen-random-project)
out of main.cpp into a new cli_random.{hpp,cpp} module. All four
use the same seeded LCG so re-runs reproduce the same content;
the gen-random-* pair shells out to scaffold-zone +
random-populate-zone + random-populate-items so the simpler
single-purpose handlers stay the source of truth.
main.cpp shrinks by 439 lines (5,788 to 5,349). The inline word
lexicons (creature names, object paths, item prefixes/nouns) move
with random-populate-zone and -items into the new module.
47th procedural mesh: 3-box vertical headstone — wide low
base plinth, tall thin main slab on top, and a slightly-
wider decorative crown that acts as a flat-cap stand-in for
the arched-top headstone shape (which would need rotated
faces). Pairs with --gen-mesh-grave (horizontal mound) and
--gen-mesh-coffin for graveyards, dungeon crypts, haunted-
zone set dressing.
Vertical layout: 15% base, 75% slab, 10% crown. Defaults to
0.60 wide x 1.10 tall x 0.18 deep with the base 1.45x wider
than the slab on each footprint axis.
Moves the three add-* coordinate-based append handlers
(--add-object, --add-creature, --add-item) out of main.cpp
into a new cli_add.{hpp,cpp} module. All three append a
single entry to a zone's JSON file with optional positional
args after the required ones. --add-item handles raw
nlohmann::json (no dedicated editor class) and auto-assigns
the smallest unused id when the user passes 0 / nothing.
main.cpp shrinks by 198 lines (5,986 to 5,788).
39th procedural texture: classic geometric spider web — N
radial spokes plus M concentric polygonal rings centered on
the image. Spoke pixels are detected by angular distance to
the nearest spoke scaled by radius, so spokes stay constant
pixel width regardless of how far they reach.
Useful for haunted house decals, dungeon corner overlays,
witch-hut interiors, magical-trap ground markers. Defaults
to 8 spokes (every 45 deg) and 5 evenly-spaced rings.
Moves the four bounds-checked remove-by-index handlers
(--remove-creature, --remove-object, --remove-quest,
--remove-item) out of main.cpp into a new
cli_remove.{hpp,cpp} module. All four share the same
load-erase-save pattern with index validation and a "what
was removed" report for audit trails. The first three use
their respective editor classes (NpcSpawner, ObjectPlacer,
QuestEditor); --remove-item walks raw nlohmann::json since
items.json doesn't have a dedicated editor class yet.
main.cpp shrinks by 145 lines (6,131 to 5,986).
46th procedural mesh: 4-box mailbox — vertical post with a
horizontal box body on top and a small flag (pole + plate)
mounted on the right (+X) side near the front. Useful for
inns, post stations, manor gates, frontier outposts, courier
routes between zones.
Defaults to a 1.10m post + 0.45L x 0.20W x 0.20H body
(~1.44m total). The box body is intentionally slightly wider
than the post on each axis so the body visually caps the
post. Flag plate extends +X away from the body so it reads
as a raised flag from the road side.
Moves the three clone-* handlers (--clone-quest,
--clone-creature, --clone-object) out of main.cpp into a new
cli_clone.{hpp,cpp} module. All three deep-copy by index,
optionally rename, and (for creature/object) offset the new
copy by 5 yards along X to prevent z-fighting with the
original. Each resets the per-entity unique id so downstream
systems that dedupe by id stay consistent.
main.cpp shrinks by 184 lines (6,315 to 6,131).
38th procedural texture: scattered bubbles done as randomly-
placed circles of varied radii, with bright rim outlines that
stay visible even where bubbles overlap (rim color wins on
any pixel that lies in any bubble's ring band).
Three colors: background, translucent-feeling fill for the
bubble interior, and a bright rim. Defaults to 50 bubbles
of radius 6-24 px with 2-px rims. Useful for water surfaces,
foam patches, soap suds, magical-effect overlays, slime
particle effects.
Moves the two quest-reward mutation handlers
(--add-quest-reward-item, --set-quest-reward) out of main.cpp
into a new cli_quest_reward.{hpp,cpp} module. Both operate on
a quest's reward struct in zone.json: the first greedy-consumes
multiple item paths in one invocation, the second uses
order-independent flag/value pairs (--xp / --gold / --silver
/ --copper) with strict 'only changed fields are written'
semantics so partial updates don't clobber unrelated fields.
main.cpp shrinks by 114 lines (6,429 to 6,315).
45th procedural mesh: 4-box signpost — stone base anchor,
tall vertical pole, thin sign board mounted face-out near
the top, and a small decorative cap. Useful for crossroads,
tavern fronts, town entrances, dungeon area markers, quest
hub indicators.
Sign board's long axis runs along Z so the player reads the
sign when facing parallel to the road. Defaults to a 2.5 m
post with a 0.8 x 0.35 m board (~2.7 m total).
Moves three contiguous quest mutation handlers (--add-quest,
--add-quest-objective, --remove-quest-objective) out of
main.cpp into a new cli_quest_objective.{hpp,cpp} module.
All three operate on a zone's quests.json with the same
QuestEditor load-edit-save flow; the objective add/remove
pair includes auto-generated description text from
type+name+count for tooltip-friendly defaults.
main.cpp shrinks by 174 lines (6,603 to 6,429). Reward and
clone-quest handlers stay inline for follow-up extractions.
37th procedural texture: basket-weave parquet flooring done as
a checkerboard of 2N x 2N cells. Half the cells are split into
two horizontal planks (wood A); the other half are split into
two vertical planks (wood B). Adjacent perpendicular pairs
form the classic interlocked basket-weave.
A third "gap" color paints thin lines along plank edges and
the cell midline, giving the inset / wood-joint appearance.
Defaults to 32-px cells with 1-px gaps. Useful for parlour
floors, manor halls, magical libraries, dwarven flooring.
Adds --import-obj as the 5th handler in cli_wom_io.{hpp,cpp},
joining its export-side sibling. The handler is the WOM-side
counterpart to --import-wob-obj (which lives in cli_world_io
because it produces WOB) — together they close the OBJ
round-trip story for both single meshes and multi-group
buildings.
main.cpp shrinks by 183 lines (6,786 to 6,603).
44th procedural mesh: 7-box water well — 4 stone walls
arranged in a hollow square (interior visible, like a real
well shaft) + 2 vertical roof posts on opposite sides + 1
horizontal cross beam at the top where rope/bucket would
mount. Useful for village squares, courtyards, dungeon
water sources, druidic shrines.
Defaults to 1.4 m square footprint with 0.8 m walls and
1.6 m roof posts (~2.4 m total height). The east/west
walls are shortened so the corner joints line up cleanly
without overlap geometry.
Moves the three open-format world-asset inspectors
(--info-wob, --info-wot, --info-woc) out of main.cpp into a
new cli_world_info.{hpp,cpp} module. Each prints a quick
structural summary (groups / portals / chunk counts /
triangles / bounds) without paying the full deserialization
cost a viewer would.
main.cpp shrinks by 144 lines (6,926 to 6,786). The
--copy-project handler that interleaved between --info-wob
and --info-wot stays inline -- it isn't an inspector and
belongs with project-mutation operations. All --json output
modes preserved.
36th procedural texture: scattered ice nuclei with 6-spike
rosettes radiating at 60 deg intervals. Each spike's pixel
intensity falls linearly from full at the seed to zero at
the end of the ray, so spikes fade naturally into the
background. Per-seed angular jitter prevents all rosettes
from aligning to the same orientation.
Useful for winter zones, ice biomes, frosted-window decals,
magical cold-effect overlays. Defaults to 80 seeds with
18-px rays in 256x256.
Moves the two audio-config audit handlers (--info-zone-audio,
--info-project-audio) out of main.cpp into a new
cli_info_audio.{hpp,cpp} module. Both surface the music /
day-ambience / night-ambience / volume settings stored in
zone.json so designers can spot zones still missing audio
assignment before a release pass.
main.cpp shrinks by 127 lines (7,053 to 6,926). Drops the
dead `label` lambda inside info-project-audio that the
unused-but-set-variable warning had been flagging. Both
--json output modes preserved.
43rd procedural mesh: 2 vertical rails + N evenly-spaced
horizontal rungs. Sits flat against +Z (the climbing face)
so it can be parented to walls, wagons, ship hulls, mage
tower trapdoors, attic openings, dungeon shafts.
Defaults to 3.0m tall × 0.6m wide with 8 rungs (typical
home/loft ladder). Rung spacing computes as height/(rungs+1)
so the first rung sits a half-step from the floor and the
last rung the same distance from the top — keeps the ladder
visually symmetric regardless of rung count.
Moves the two content-density audit handlers
(--info-zone-density, --info-project-density) out of main.cpp
into a new cli_info_density.{hpp,cpp} module. Both count
creatures/objects/quests per tile to surface sparse zones
(boring) and over-stuffed ones (frame-rate bombs); the project
variant rolls per-zone numbers into a project-wide table with
per-zone averages for content-pacing reviews.
main.cpp shrinks by 201 lines (7,254 to 7,053). Both --json
output modes preserved for balance-audit pipelines.
35th procedural texture: roof shingles done as half-row-staggered
rows of rectangular tiles. Three colors: shingle base, a shadow
band at the top of each row (where the row above overlaps), and
thin vertical seams between adjacent shingles in the same row.
The half-row stagger comes from the standard roofing convention
of offsetting alternate courses by half a tile width — gives the
classic interlocked look that pure brick patterns lack.
Useful for roofs, scale armor close-ups, fish bellies, anywhere
needing tightly-packed offset rectangles. Defaults to 32x24
shingles with 4-px shadow + 1-px seams.
Moves the two water-layer audit handlers (--info-zone-water,
--info-project-water) out of main.cpp into a new
cli_info_water.{hpp,cpp} module. Both aggregate liquid data
(water/ocean/magma/slime) across MH2O chunks; the project
variant rolls per-zone counts into a project-wide total with
height range and per-type histogram.
main.cpp shrinks by 209 lines (7,463 to 7,254). Both --json
output modes preserved for capacity-planning pipelines.
42nd procedural mesh: 8-box bed — 4 corner legs, mattress
slab, tall headboard at the +Z end, shorter footboard at the
-Z end, and a small pillow on the mattress near the headboard.
Pairs with --gen-mesh-table / --gen-mesh-bookshelf for inn
rooms, manor bedrooms, barracks, dungeon cells.
Defaults to 2.0 × 1.2 (length × width) with 0.30 leg height
and 0.20 mattress thickness — proportions of a single bed.
Customizable per dimension so the same primitive covers
single, double, and king-size beds plus child cots.
Moves the two spatial-bounds info handlers (--info-zone-extents,
--info-project-extents) out of main.cpp into a new
cli_info_extents.{hpp,cpp} module. Both compute world-space XYZ
bounding boxes from manifest tile coords + per-chunk height
samples; the project variant unions every zone's box into one.
main.cpp shrinks by 232 lines (7,695 to 7,463). Both --json
output modes preserved for camera-framing pipelines.
34th procedural texture: cathedral / magical-window stained
glass done as a Voronoi-cell tessellation. Each pixel snaps to
its nearest seed point; pixels near a cell boundary (small
relative gap to the second-nearest seed) become the lead color
producing the leaded-glass dividers between colored regions.
Three stained colors cycle across cells (cellIdx % 3) for a
balanced palette without per-cell color authoring. Defaults to
32 cells in 256x256, using ratio-based boundary detection so
lead-line thickness scales naturally with cell density.
Useful for cathedral windows, mage tower decals, magical
portals, ritual circle backdrops.
Moves the two disk-byte audit handlers (--info-zone-bytes,
--info-project-bytes) out of main.cpp into a new
cli_info_bytes.{hpp,cpp} module. Both categorize files by
extension into open / proprietary / derived buckets — the
project-wide variant is the headline metric for tracking
the open-format migration's progress against the .m2/.wmo/
.blp/.dbc baseline.
main.cpp shrinks by 244 lines (7,939 to 7,695). Both --json
output modes preserved for machine-readable reports.
41st procedural mesh: 4-box urban lighting fixture — square
base plinth, tall thin vertical pole, lantern body box around
the pole top (overlaps so the lamp visually caps the pole),
and a slightly-wider cap plate that reads as an awning.
Useful for streets, plazas, courtyards, taverns — anywhere
that wants explicit lighting fixtures without modeling each
one by hand. Defaults to 3-meter pole + 0.5-meter lantern
(~3.6m total). Customizable per dimension to cover everything
from candlestick-stands to tall ornate streetlamps.
Moves the two tree-style content browser handlers
(--info-zone-tree, --info-project-tree) out of main.cpp into
a new cli_info_tree.{hpp,cpp} module. Both render
Unix-`tree`-style hierarchical views — one drilling into a
single zone (manifest, tiles, creatures, objects, quests,
files) and one giving a bird's-eye view of every zone in a
project with bake/viewer status.
main.cpp shrinks by 201 lines (8,140 to 7,939). The remaining
info-zone/-project-* pairs (bytes, extents, water, density,
audio) form the next natural extraction batch.
33rd procedural texture: fish / dragon / chain mail scales done
as a half-row-staggered grid of circles whose centers sit at the
bottom-center of each cell. Adjacent rows offset by half a cell
width, with circle radius slightly larger than half the cell, so
the circles interlock into the classic overlapping-scale look.
Three colors: background fills the gaps, scale body fills most of
each circle, and a rim color highlights the top arc to give the
raised / armored feel.
Defaults to 24×16 cells; scaled by cellW × cellH for fine control
of scale density. Useful for chain mail, dragonhide, fish skin,
roof shingles, anywhere needing tiled curved scales.
Moves all six world-asset interchange handlers (--export-wob-glb,
--export-wob-obj, --import-wob-obj, --export-whm-glb,
--export-whm-obj, --export-woc-obj) out of main.cpp into a new
cli_world_io.{hpp,cpp} module. WOB / WHM / WOC are our open
replacements for proprietary WMO / ADT-heightmap / ADT-collision
data; these are the bridge that lets the open formats round-trip
through Blender, MeshLab, Three.js, and the rest of the standard
3D toolchain.
main.cpp shrinks by 858 lines (8,997 to 8,140). The single-mesh
--import-obj handler stays inline for now -- it shadow-mirrors
cli_wom_io's --import-stl semantics and will move there next.
40th procedural mesh: simple 5-box table — flat top slab on
4 vertical corner legs. Pairs with --gen-mesh-bench /
--gen-mesh-throne / --gen-mesh-bookshelf for taverns,
dining halls, libraries, and study set dressing.
Defaults to 1.6 × 1.0 base × 0.85 tall, 0.10-square legs,
0.06-thick top — sensible dining-table proportions.
Customizable per dimension so a single primitive covers
desks, end tables, and big banquet tables.
Moves the four WOM interchange-format handlers (--export-obj,
--export-glb, --export-stl, --import-stl) out of main.cpp into
a new cli_wom_io.{hpp,cpp} module. WOM is our open M2
replacement; these are the bridge that lets it round-trip
through every external 3D tool — Blender, Three.js, slicers,
CAD packages — so the open format is actually useful.
main.cpp shrinks by 467 lines (9,464 to 8,997). The five WOB
and WHM exporters (--export-wob-glb, --export-whm-glb, etc.)
remain inline for a follow-up extraction.
32nd procedural texture: classic V-shaped herringbone done as
horizontal strips of parallel slanted lines whose slant
direction flips every strip. Implementation is a per-pixel
shear: shifting x by the row's local-y collapses each diagonal
into a vertical band in shifted-x space, so a single modulo
picks line vs background.
Useful for parquet floors, brick patios, fabric weaves, fish
scale-style chain mail, anywhere needing a strong directional
pattern. Two-color, defaults to 32-px strips with 12-px line
spacing and 4-px line width.
Moves all 4 GLB introspection handlers (--validate-glb /
--info-glb shared, --info-glb-tree, --info-glb-bytes,
--check-glb-bounds) out of main.cpp into a new
cli_glb_inspect.{hpp,cpp} module. GLB is our open replacement
for proprietary M2/WMO bake outputs, so these belong with the
other open-format tooling.
main.cpp shrinks by 657 lines (10,121 to 9,464). Every
handler preserves its --json output mode for machine-readable
reports.
39th procedural mesh: 5-panel cabinet (back / left / right /
top / bottom) divided into N bays by N-1 horizontal shelves,
with rows of pseudo-random book boxes on each level. Book
widths and heights vary per bay (seeded by bay index so
re-generating the same shelf gives the same layout) so the
result reads as a stocked library instead of a perfect grid.
Defaults to 1.5x2.0x0.4 with 4 shelves (~72 books). Useful
for studies, libraries, mage towers, anywhere that needs
filled-out furniture set dressing.
Moves the four structural validators for INTEROP file formats
(--validate-stl, --validate-png, --validate-blp, --validate-jsondbc)
out of main.cpp into a new cli_validate_interop.{hpp,cpp} module.
These check files coming in/out of wowee from third-party tools,
distinct from cli_format_validate.cpp which validates the native
open formats (WOM, WOB, WOC, WHM).
main.cpp shrinks by 524 lines (10,644 to 10,121). Each validator
preserves its --json output mode for machine-readable reports.
31st procedural texture: classic argyle pattern done by working
in a 45-rotated coord system (u, v) = (x+y, x-y) so lozenges
become axis-aligned squares for the checkerboard step. Diagonal
stitch lines fall out of u%cell and v%cell crossing zero.
Three-color: A/B for the alternating diamond fill, third color
for the diagonal stitch overlay. Defaults to 64-pixel cells with
2-pixel stitches. Useful for sweater fabric, seat cushions,
heraldic banner overlays.
Moves the five single-file format converters (--convert-m2,
--convert-wmo, --convert-dbc-json, --convert-json-dbc,
--convert-blp-png) out of two dedicated post-loop blocks in
main.cpp into a new cli_convert_single.{hpp,cpp} module. The
batch wrappers in cli_convert.cpp keep working — they shell
out to wowee_editor with these flags.
main.cpp shrinks by ~335 lines (10,979 → 10,644). The two
trailing for-loops were dead code after wiring the dispatcher
into the main loop, so they're gone too.
38th procedural mesh: classic 6-sided coffin with the
narrow-head / wide-shoulder / tapered-foot top-down profile
that's instantly recognizable from any angle. Built as 6 side
quads + top lid fan + bottom panel fan, all with face-shared
normals so it shades cleanly under any lighting.
Pairs with --gen-mesh-grave for graveyard set dressing.
Defaults to 2.0×0.8×0.6 (length × shoulder-width × height).
Moves the four schema-migration handlers (--migrate-wom,
--migrate-zone, --migrate-project, --migrate-jsondbc) out of
main.cpp into a new cli_migrate.{hpp,cpp} module. Same
behavior — they're idempotent in-place upgraders for older
WOM revisions and JSON DBC sidecar schemas. main.cpp shrinks
by 282 lines (11,261 → 10,979).
3-color crossing-band pattern: 6-band repeat sequence (A A B
C C B) per axis, with the pixel color at any position being
the average of the horizontal-band and vertical-band colors.
That averaging at intersections produces the characteristic
diamond grid of Scottish tartans without explicit "weave"
math — the band overlap pattern just falls out.
Defaults: bandPx=32 (repeat=192px). Useful for clan banners,
kilts, blanket textures, fabric set dressing. Brings the
procedural texture pattern set to 33.
Moves the 3D-export bake handlers out of main.cpp:
--bake-zone-glb --bake-zone-stl --bake-zone-obj
--bake-project-obj --bake-project-stl --bake-project-glb
The STL + GLB project bakes share a combined dispatcher (one
function with internal STL-vs-GLB branching) since they walk
the same per-zone asset list and only differ in the output
emission code.
main.cpp drops 12,119 → 11,261 lines (-858). The combined-OR
opener spanning multiple lines created a parse-error fragment
in the extraction; caught + manually fixed before commit
(same pattern as the WOM info attachments/particles/sequences
extraction).
5-box composite: low pedestal slab + seat block + tall
vertical backrest at -Z + 2 small armrests on the +X/-X
sides. Pedestal is wider than the seat for a stable
foundation, seat thickness is 30% of total seat height.
Defaults: seatW=0.8, seatH=0.5, backH=1.5, pedSize=1.2.
Useful for throne rooms, hero seats, judgement halls,
royal court set dressing. Brings the procedural mesh
primitive set to 37.
Moves the report-export handlers (md / csv / html / sha256 /
graphviz) for zone & project audits out of main.cpp:
--export-zone-summary-md --export-zone-csv
--export-zone-checksum --export-project-checksum
--validate-project-checksum --export-zone-html
--export-project-html --export-project-md
--export-quest-graph
Also moves the file-scope wowee_sha256 namespace (the SHA-256
implementation that the checksum exporters use) into the new
module's anonymous namespace — it had no other callers in
main.cpp so no cross-TU coupling needed.
main.cpp drops 13,120 → 12,119 lines (-1,001). Build error
during extraction (missing #include <unordered_set>) caught
and fixed.
Vertical color gradient from dark (bottom) to hot (top), mixed
with multi-octave smooth noise so the flame boundary wavers
randomly rather than reading as a clean horizontal line.
Vertical position curve is squared so the dark stays dark
longer and the hot saturates faster — matches real-flame
appearance where most of the body is dark with a bright tip.
Useful for torches, braziers, magical effects, lava-zone set
dressing, fireplace texture details. Brings the procedural
texture pattern set to 32.
Moves the four extracted-Data-tree audit handlers out of main:
--info-extract (per-extension counts + bytes)
--info-extract-tree (per-directory rollup)
--info-extract-budget (proprietary share + open-format gap)
--list-missing-sidecars (find unconverted .m2/.wmo/.blp/.dbc)
All four operate on a Blizzard-format extracted Data tree —
they audit what's there and what's missing in the migration
from proprietary formats to open ones.
main.cpp drops 13,485 → 13,120 lines (-365). Behavior verified
by re-running --info-extract on a test zone (same output).
Square cage frame: top + bottom thin slabs + 4 corner posts
(thicker than bars) + N evenly-spaced bars per side. Bars on
each side span perpendicular to that side's plane so the
cage reads as enclosed from any viewing angle.
Defaults: width=1.5, height=2.0, barsPerSide=5, barR=0.04.
Useful for prison cells, animal pens, dungeon set dressing,
caged exhibits. Brings the procedural mesh primitive set to 36.
Moves the items.json read-only inspection handlers out of
main.cpp:
--list-items --info-item
--validate-items --validate-project-items
--info-project-items
Item editing handlers (--add-item, --set-item, --remove-item,
--add-quest-reward-item) stay in main.cpp since they share
state with quest reward editing logic and would need a
broader extraction.
main.cpp drops 13,887 → 13,485 lines (-402). Behavior
verified by re-running --list-items on a non-items zone
(same error message).