Commit graph

4275 commits

Author SHA1 Message Date
Kelsi
dadb08baee feat(editor): add --gen-mesh-dock pier-on-stilts primitive
58th procedural mesh primitive. Builds a wooden waterside
dock from axis-aligned boxes:

  • flat plank deck slab spanning length × width at deckT
    thickness, sitting at deckHeight off the ground
  • N pairs of square pilings under the long edges, evenly
    spaced from end to end
  • pilings inset by pilingW so the deck overhangs slightly
    (the standard "boards rest ON TOP of the posts" look)

Distinct from --gen-mesh-bridge which arches OVER an
obstacle — a dock walks straight out from a shoreline on
stilts to the water. Useful for fishing villages, port
warehouses, lake settlements, ferry stations, smuggler
hideouts.

Watertight under weld (--audit-watertight passes): pilings
sit below the deck overhang and don't share corners with it.
2026-05-09 11:17:28 -07:00
Kelsi
3cf3b35885 refactor(editor): extract edge classification into cli_weld
The "loop over triangles, key edges by canonical-vertex pair,
count uses, classify boundary/manifold/non-manifold" pass was
duplicated across cli_mesh_info, cli_world_info, and the new
cli_audits watertight check. Hoist it into cli_weld as
classifyEdges(indices, canon) returning an EdgeStats struct
with boundary / manifold / nonManifold counters and a
watertight() convenience method.

All three callers verified byte-identical:
  • --info-mesh-stats firepit:    180 edges, watertight YES
  • --info-wob-stats cube:        18 manifold, watertight YES
  • --audit-watertight /tmp/...:  61 meshes, 12 failures, rc=12

About 60 more lines of duplication removed; classifyEdges +
buildWeldMap together form the complete reusable surface for
new weld/topology audit commands.
2026-05-09 11:15:31 -07:00
Kelsi
0137ca8707 feat(editor): add --gen-texture-caustics water-shimmer pattern
52nd procedural texture: water-surface caustics via four
superimposed sine waves running along x, y, x+y, and x-y.
Each wave is taken in absolute value before multiplication
so peaks shine on either side of the wave centerline,
giving the bright-line interference network you see on a
pool floor or sunlit shallow streambed.

Two-color lerp from bgHex (depth) to hiHex (highlight).
Default 24-pixel period reads at the right scale for water-
plane underlay; pass a smaller period for tighter ripple.

Useful as the floor texture under transparent water
volumes, magic-fountain pools, fish-tank ground plates,
ritual scrying basins, and any "submerged surface seen
through clear water" effect.
2026-05-09 11:12:28 -07:00
Kelsi
89b7e2f505 feat(editor): add --audit-watertight project-level QA tool
Walk every .wom under <zoneDir|projectDir>, run the welded
watertight check from cli_weld + the same edge-analysis as
--info-mesh-stats, and report PASS/FAIL with the per-mesh
failure detail (boundary / non-manifold edge counts).

Exit code is the number of failures (capped at 255), so
CI pipelines can gate on `--audit-watertight $project` and
fail the build if any mesh isn't a closed solid.

Smoke-tested over 61 procedurally-generated WOMs:
  • 49 PASS — most stand-alone primitives are watertight
  • tent_fixed FAIL with 5 boundary edges = the intentional
    door cutout (correct surface count)
  • woodpile / bed / well variants FAIL with non-manifold
    edges = adjacent stacked cylinders/legs sharing corners
    (correct geometry callout)

Defaults to weld eps 1e-4 — a good balance for procedural
output where positions are exact rationals at typical scales.
2026-05-09 11:11:09 -07:00
Kelsi
e732894b4c feat(editor): add --gen-mesh-haystack terraced farm haystack
57th procedural mesh primitive. Builds a layered haystack:
N stacked frustums, each smaller than the one below, with
the topmost layer tapering to an apex point. The visible
shelves where each layer overhangs the smaller one above
read as bound straw shocks rather than the smooth
silhouette --gen-mesh-pyramid produces.

Per layer: vertical side wall ring (no taper within a
single layer) plus a top "shelf" annulus where it meets
the next layer up. Final layer is a cone fan to a single
apex vertex. Closed bottom disc keeps the model a solid
for collision baking — verified watertight under weld.

Useful for farms, granaries, stables, harvest-festival
clearings, druid groves with bound bundles. Default 3
layers at 12 sides reads cleanly at typical NPC scale.
2026-05-09 11:08:39 -07:00
Kelsi
869124fa96 refactor(editor): extract vertex weld into shared cli_weld utility
Three callers were each open-coding the same quantize-and-
bucket pass over vertex positions: --info-mesh-stats,
--info-wob-stats, and --bake-wom-collision. Move the
implementation to cli_weld.{hpp,cpp} as buildWeldMap() and
have each caller pass a flat positions array.

Identical std::map-based exact-equality keying preserved
(unbalanced-hash collisions remain absent). All three call
sites verified to produce byte-identical output:

  • info-mesh-stats firepit: 240→80 verts, 0 boundary
  • info-wob-stats cube:     8→8 verts, watertight YES
  • bake-wom-collision tent: 18→6 verts, 8-tri WOC

About 75 lines of duplication removed; future weld-using
commands now opt in by including one header.
2026-05-09 11:05:54 -07:00
Kelsi
9031bdb620 feat(editor): add --gen-texture-rope twisted-cordage pattern
51st procedural texture: two interleaved sinusoidal strands
running along the Y axis. Each strand's X position oscillates
as W/4·sin(2π·y/period); the second strand is phase-shifted
by π so the two snake around each other forming the classic
helical twist of a tightened rope.

Within each strand, brightness rises from 0.55 at the edge
to 1.0 at the centerline (cos² falloff) — gives the rounded
3D appearance of cylindrical fibers without a separate
shadow pass.

Useful for hanging ropes, ship rigging, tied-bundle textures,
suspension bridges, market-stall awning ties, and any
"twisted cord" surface where a flat color would read as
ribbon rather than rope.

Default 24-pixel period with 8-pixel strands renders cleanly
at 256x256 with about 10 visible twists.
2026-05-09 11:03:33 -07:00
Kelsi
069e76057e feat(editor): add --bake-wom-collision WOM→WOC pipeline
Convert any WOM mesh into a WOC collision file via
WoweeCollisionBuilder::addMesh, with optional --weld <eps>
that merges shared positions into a single canonical index
before baking. The weld pass is what makes per-face-shaded
procedural primitives produce a properly indexed collision
mesh — adjacent faces that previously authored 4 unique
verts per face now share 1 corner across all incident
faces, so raycasts can traverse edges naturally.

Also accepts --steep <deg> to control the walkable/steep
classification threshold (default 50° from horizontal,
matching the terrain pipeline).

Smoke-tested end-to-end:
  • firepit: 240 verts → WOC with 120 tris (20 walkable
    floor, 20 steep stone walls, rest neutral)
  • tent_solid: 18 verts welds to 6, produces 8-tri WOC
  • canopy: 216 → 56 weld, 108 tris, 30° steep cut

Pairs naturally with --info-mesh-stats --weld for
collision-quality QA before baking.
2026-05-09 11:01:45 -07:00
Kelsi
51d884c248 feat(editor): add --gen-mesh-canopy market-stall awning
56th procedural mesh primitive. Builds a rectangular awning
on 4 corner posts with a flat overhead panel and optional
drape lips hanging down from each edge:

  • 4 corner posts inset by postR so their outer faces line
    up flush with the panel edges above
  • flat panel slab spanning the full footprint, sitting on
    top of the posts
  • optional drape lips on all four panel edges (set drape=0
    for a clean flat-top canopy with no hanging fabric)

All axis-aligned boxes; the no-drape variant is fully
watertight (verified via --info-mesh-stats --weld). Useful
for market-square stalls, vendor stations, alchemist booths,
caravan rest stops, and tournament viewing platforms.
2026-05-09 10:59:05 -07:00
Kelsi
4112b6d257 feat(editor): --info-wob-stats + fix weld hash collision bug
Add --info-wob-stats reporting per-group + aggregate
triangle counts, surface area, edge analysis, and watertight
check for WOB buildings. Same flag surface as --info-mesh-stats
including --weld <eps> for true topological closure check
on per-face-vertex meshes.

Also fixes a correctness bug in the weld implementation
of --info-mesh-stats: the previous code used a 64-bit hash
of the quantized position as the equality key, which gave
false-positive collisions that incorrectly merged distinct
vertices. A unit cube's 8 corners collapsed to 2 positions
under the buggy hash. Replace with std::map keyed on the
actual quantized (qx, qy, qz) tuple so equality is exact.

Re-verified: cube 8→8 watertight YES; firepit 240→80
watertight YES (was wrongly reporting 56 unique with 48
non-manifold edges); tent_solid 18→6 watertight YES;
tent_fixed 21→9 with 5 boundary edges at the door perimeter
(correct — door is intentionally open).
2026-05-09 10:57:22 -07:00
Kelsi
a7989cc7ab feat(editor): add --gen-texture-corrugated metal-sheet ridges
50th procedural texture: smooth cosine ridges that lerp
between bgHex (trough) and hiHex (crest) over a configurable
period. Direction defaults to vertical ridges (wave varies
along X) — the standard corrugated-sheet-metal-roof look —
with optional horizontal mode for siding panels and pipe
texturing.

Useful for sheet-metal roofing, corrugated-iron walls,
sci-fi paneling, and any "ridged plate" surface where the
existing flat --gen-texture-metal would read as too uniform.

Default 16px period at 256x256 reads cleanly with about 16
ridges across the tile.
2026-05-09 10:50:46 -07:00
Kelsi
4455a4eb6f feat(editor): --info-mesh-stats --weld <eps> for true topology check
Procedural primitives use per-face vertex layouts so flat
shading works (each box face gets its own 4 corners with a
unique normal). That makes them read as "not watertight" at
the index level even when the geometry is visually closed.

The new --weld <eps> flag quantizes vertex positions onto a
1/eps grid, hashes each cell, and remaps every duplicate to
the canonical (lowest-index) representative before edge
analysis. Edges that cross weld boundaries are then unified
and the manifold check measures topological closure of the
underlying surface.

Validates the full plumbing: tent_solid welds 18 verts → 6
positions and reports YES watertight; tent (with door) welds
21 → 9 and reports the 5-edge door seam as open boundary;
firepit welds 240 → 56 with 0 boundary but 48 non-manifold
edges — the 4-stone corners where adjacent ring stones share
a position, making the surface branchy.
2026-05-09 10:49:28 -07:00
Kelsi
c206216676 feat(editor): add --gen-mesh-woodpile firewood-stack primitive
55th procedural mesh primitive. Builds the classic 3-2-1
firewood pyramid:

  • 3 cylindrical logs along the bottom row, sitting on
    the ground at radii apart
  • 2 logs nestled in the gaps of the bottom row
  • 1 log crowning the pile

Vertical step uses cos(30°) = sqrt(3)/2 so adjacent logs
touch tangentially, giving the close-packed look of a real
chopped-wood stack. Logs lie horizontally along the Z axis
with closed end caps; sides parameter controls cross-
section smoothness (default 12, range 6..64).

Pairs naturally with --gen-mesh-firepit and --gen-mesh-tent
for outdoor camp / homestead / lumberyard set dressing.
2026-05-09 10:47:21 -07:00
Kelsi
afdf754c13 fix(editor): correct --gen-mesh-tent door fan triangulation
The +X gable's door-fan was wrong: it emitted 5 triangles
including {b, c, br} which spanned across the door cutout
area, and left bl unreferenced. --info-mesh-stats caught
both — surface area was over-reported by 0.10 m^2 and one
triangle was degenerate.

Replace with the correct 4-triangle fan from R1 around the
six-vertex polygon B → bl → dt → br → C → R1 (the original
gable triangle minus the bl-br-dt door notch). All six
vertices are now referenced, no triangle covers the door
area, and surface area matches the visible canvas.
2026-05-09 10:45:27 -07:00
Kelsi
f8bd4c70c8 feat(editor): add --gen-texture-planks floor-board pattern
49th procedural texture: horizontal plank-floor pattern.
Each plank gets:

  • a hash-derived per-plank brightness offset (-24..+24)
    so adjacent boards read as separate pieces of wood
    rather than one long stripe
  • a 1px horizontal seam at its bottom edge
  • a 1px vertical end-seam at a hash-jittered x position,
    staggering plank ends across rows
  • optional darker grain streaks at evenly-spaced columns
    (jittered per plank so the pattern doesn't grid-align)

Useful for inn/tavern floors, ship deck planking, bridge
surfaces, market-stall counters, and any wood-floor surface
where the existing single-grain --gen-texture-wood would
read as one giant board.

Default 16px plank height with 5 grain streaks gives a
recognizable floor at 256x256 without aliasing.
2026-05-09 10:44:13 -07:00
Kelsi
5e404a9fe6 feat(editor): add --info-mesh-stats geometric audit
Reports total surface area, per-triangle area histogram
(min/max/mean/median), edge analysis (boundary / manifold /
non-manifold counts), watertight check, and degenerate
triangle count for a single WOM.

Watertightness here is the topological notion: every edge
must be shared by exactly 2 triangles via shared vertex
indices. This is what collision bakes and physics queries
actually need — visually-closed primitives whose adjacent
faces don't weld vertices will (correctly) report as
non-watertight.

Already caught a real defect in handleTent's door-fan
triangulation: the fan covers the door cutout area with
a stray triangle and leaves a vertex unreferenced.
Edge analysis is gated by triCount <= 2M to keep the
unordered_map bounded for huge baked terrain meshes.
2026-05-09 10:41:58 -07:00
Kelsi
17a53f192f feat(editor): add --gen-mesh-firepit camp firepit primitive
54th procedural mesh primitive. Builds a recognizable
campfire setup from axis-aligned boxes only:

  • ring of N stone cubes evenly placed at radius ringR
    around the firepit center, sitting on the ground
  • two crossed log boxes at the center (one along X,
    one along Z, slightly raised) — the unmistakable
    visual cue separating a firepit from a generic
    decorative stone ring

Pairs naturally with --gen-mesh-tent for outdoor camp
set dressing (Horde encampments, Defias hideouts, scout
overlooks). Default 0.5-radius ring with 8 stones reads
cleanly at 1:1 player scale.
2026-05-09 10:37:37 -07:00
Kelsi
86377df7ad refactor(editor): table-driven --gen-texture-* dispatcher
Mirror the kMeshTable pattern from cli_gen_mesh.cpp: replace
the 48-row handcoded if/strcmp chain in handleGenTexture
with a static TextureEntry table that the dispatcher walks
linearly. minNextArgs preserves the per-flag arg-count guards
(noise needs 1, gradient needs 3, stained-glass needs 5)
so missing-arg behavior is byte-identical to the old chain.

Each new texture primitive now lands as a one-line table
append instead of another paste-fest at the bottom of the
dispatcher. Saves ~80 lines.
2026-05-09 10:35:31 -07:00
Kelsi
033c929576 feat(editor): add --gen-texture-chainmail interlinked rings
48th procedural texture: brick-offset ring outlines. Even
and odd rows are shifted by half a cell width so each ring
interlocks visually with its neighbors above and below —
the classic chainmail-armor pattern. Each pixel is tested
against the nearest ring center; if its distance lies
inside [ringR - strokeW/2, ringR + strokeW/2] it's
painted as the ring color, otherwise background.

Useful for armor textures (mail tunics, helms, gauntlets),
metallic fabric set dressing on guard NPCs, and dungeon
gate/grate textures. Default ring radius 5 on a 14x10
brick spacing reads cleanly at 256x256 without aliasing.
2026-05-09 10:34:02 -07:00
Kelsi
190cb3d3fa refactor(editor): table-driven --gen-mesh-* dispatcher
Collapse the 60-entry handcoded if-chain in handleGenMesh
into a static MeshEntry table { flag, minNextArgs, fn } that
the dispatcher walks linearly. Saves ~120 lines and makes
adding a primitive a single-row append instead of a paste-
fest. Also drop a dead inner-ring computation block in
handleArchway and an unused barCY local in handleCage that
were producing -Wunused-but-set-variable warnings — full
project now compiles cleanly with no warnings.
2026-05-09 10:32:26 -07:00
Kelsi
e746e400dd feat(editor): add --gen-mesh-tent A-frame canvas tent
53rd procedural mesh primitive. Builds a watertight A-frame
tent:
  • two sloped roof panels meeting at a ridge that runs along
    the X axis
  • two triangular gables sealing the ends
  • optional inverted-V door notch carved out of the +X gable
    (parameterized by doorH and doorW; either set to 0 disables
    the cutout for a solid gable)
  • bottom face for collision-bake watertightness

Useful set dressing for Horde encampments, troll camps,
Defias bandit hideouts, scout/quartermaster overlooks, and
generic outdoor quest hubs.

Default footprint 1.6 x 1.0 with 0.9 ridge height and a
0.5 x 0.4 door notch — all dimensions overridable on the CLI.
2026-05-09 10:25:38 -07:00
Kelsi
ff82c0ade2 refactor(editor): table-driven multi-arg flag validation
Collapse main.cpp's hand-written ladder of 28 individual
"if --foo and i+N >= argc, print message, return 1" blocks
into a single loop over kMultiArgRequired (struct of flag,
needed count, synopsis).

main.cpp drops from 246 → 126 lines. Adding a new multi-arg
flag now means appending one row to cli_multi_arg_required.cpp
instead of pasting another six lines into the validation
ladder. Mirrors the kArgRequired pattern that handles
single-arg flags.
2026-05-09 10:22:36 -07:00
Kelsi
a9f4e322d5 feat(editor): add --gen-texture-knit V-stitch fabric pattern
47th procedural texture: knit fabric V-stitch — each stitch
occupies a cellW x cellH cell holding the V-shape made by
two diagonal strokes meeting at the apex (cellW/2, 0) and
dropping to the cell's bottom corners. Cells tile contiguously
in both axes giving the iconic chevron-zigzag appearance of
knitted fabric stitches.

Useful for sweater fabric, woolly NPC clothing, blanket
textures, mitten/scarf set dressing, dwarven knitwork.
Defaults to 16x12 cells with 2-px stroke width.
2026-05-09 10:18:22 -07:00
Kelsi
0ff13ccd67 refactor(editor): replace 60-handler chain with table-driven dispatch
Adds cli_dispatch.{hpp,cpp} containing a static table of every
extracted handler family's dispatch function. The table-walker
tryDispatchAll() iterates the table once per argv token, calling
each handler in turn. handleConvertSingle stays as a special-
case call in main.cpp because it threads dataPath through.

main.cpp shrinks from 486 to 236 lines (-250). Adding a new
handler module now requires touching only cli_dispatch.cpp's
include list + table — no main.cpp edits, no growing
if-else chain. The 60+ #include lines for individual cli_*
modules collapse to one #include for cli_dispatch.hpp.
2026-05-09 10:15:51 -07:00
Kelsi
3db5d05519 feat(editor): add --gen-mesh-archway-double twin-passage primitive
58th procedural mesh: 5-box double archway — 3 vertical posts
(left / shared center / right) plus 2 horizontal lintels each
spanning one opening. Pairs with the existing single
--gen-mesh-archway for plaza approaches, double-door tomb
fronts, formal garden entrances, paired temple entries.

Defaults to 1.40m × 2 openings with 2.40m opening height
(~3.16m total width × 2.60m height including lintels).
2026-05-09 10:11:24 -07:00
Kelsi
0e8ef746af refactor(editor): extract kArgRequired + meta handlers into cli modules
Splits the 138-line static-local kArgRequired array (the list
of flags requiring positional args) into a new
cli_arg_required.{hpp,cpp} module with extern linkage. Then
moves the three meta handlers that depended on it out of
main.cpp into cli_introspect.cpp:
- --validate-cli-help (uses the array for self-check)
- --version / -v (3-line printf)
- --help / -h (1-line passthrough)

main.cpp shrinks by 191 lines (677 to 486). Both the early
missing-argument detector in main() and --validate-cli-help
in cli_introspect.cpp now share one source of truth for the
arg-required list.
2026-05-09 10:08:14 -07:00
Kelsi
f6f5b7d9a0 feat(editor): add --gen-texture-zebra wavy-stripe pattern
46th procedural texture: zebra-print stripes — base
horizontal stripes with a sinusoidal y-shift in x so they
undulate organically rather than aligning to the row grid.
Two-color (bg + stripe) for the iconic black-on-white animal-
print effect. Each pixel computes y + amplitude*sin(2π*x/wavelength)
then mods by the stripe period.

Useful for animal-print fabric, savanna grass mats, tribal
clothing, fur tiles. Defaults to 24-px period with 8-px
amplitude on an 80-px wavelength wave.
2026-05-09 10:03:27 -07:00
Kelsi
32e7eef107 refactor(editor): extract zone-level export handlers into cli_zone_export.cpp
Moves three per-zone visual / report exporters (--export-png,
--export-zone-deps-md, --export-zone-spawn-png) out of
main.cpp into a new cli_zone_export.{hpp,cpp} module.
- export-png renders heightmap + normal-map + zone-map PNG
  triplet for terrain previews
- export-zone-deps-md emits a GitHub-renderable model
  dependency table (PR-friendly counterpart to --list-zone-deps)
- export-zone-spawn-png plots top-down spawn distribution PNG
  bound to the zone's tile range

main.cpp shrinks by 267 lines (944 to 677) and crosses
below 700 lines on the way down. The previous --info-tilemap
dump used a 64x64 grid; this set complements it with content-
relative views.
2026-05-09 10:01:33 -07:00
Kelsi
09d21f08dd feat(editor): add --gen-mesh-brazier fire-pit primitive
57th procedural mesh: 7-box brazier — square base plate,
narrow vertical stem, wider bowl on top of the stem, and 3
flame boxes of varying heights rising from the bowl. The
flame layout is a triangle (tallest center, two shorter on
either side) so the silhouette reads as fire rather than a
uniform block.

Useful for dungeons, temples, watchtowers, throne rooms,
goblin camps — anywhere a fantasy world needs visible light
sources. Defaults to 0.55m bowl on a 0.80m stem (~1.28m
total height).
2026-05-09 09:56:38 -07:00
Kelsi
4f72fe6222 refactor(editor): extract project actions into cli_project_actions.cpp
Moves three project-level handlers (--copy-project,
--zone-summary, --bench-bake-project) out of main.cpp into a
new cli_project_actions.{hpp,cpp} module. None of these fit
the more-specific modules: copy-project does recursive
project-tree duplication, zone-summary collapses validate +
content rollup into one CI-friendly command, bench-bake-project
times per-zone WHM/WOT load to track perf regressions.

main.cpp shrinks by 243 lines (1,187 to 944). Both --json
output modes preserved for CI pipelines.
2026-05-09 09:54:17 -07:00
Kelsi
c4ff7e583c feat(editor): add --gen-texture-leopard animal-print pattern
45th procedural texture: leopard print spots done as the
union of 4 small overlapping sub-circles per spot. The
sub-circle offsets are jittered per-spot so each spot has
an irregular non-circular silhouette without authoring per-
spot polygons. Two-color (bg + spot) for the classic
leopard look.

Useful for animal-print fabric, fur tiles, druidic robes,
shamanic hide drums, hunter armor textures. Defaults to 60
spots of ~8-px radius across 256×256.
2026-05-09 09:51:58 -07:00
Kelsi
25660a0d8b refactor(editor): move 2 more mesh-info handlers into cli_mesh_info.cpp
Adds --info-mesh-storage-budget and --info-project-models-total
to the existing cli_mesh_info module so all 5 mesh-aggregate
info handlers live together. The first computes per-category
byte breakdowns for a single WOM (positions / normals / UVs /
indices / bones / animations); the second walks every zone in
a project for an aggregate WOM/WOB rollup with per-zone rows.

main.cpp shrinks by 250 lines (1,437 to 1,187). All five
handlers preserve --json output for capacity-planning pipelines.
2026-05-09 09:49:39 -07:00
Kelsi
b1f6563c0a feat(editor): add --gen-mesh-podium ceremony-platform primitive
56th procedural mesh: stepped pyramid speaker stand —
configurable number of steps (2..8) tapering from base to top
with equal step heights for visual rhythm, plus a small
lectern box positioned at the back of the top platform so a
speaker has room in front. Top platform is half the base
footprint, with each step shrinking by an equal increment.

Useful for throne rooms, ceremonial dais, NPC speaker
positions, monument bases, judgment platforms. Defaults to
1.60m base × 3 steps with a 0.30m lectern (~0.96m total).
2026-05-09 09:46:59 -07:00
Kelsi
fca17592b1 refactor(editor): extract zone-data maintenance into cli_zone_data.cpp
Moves three derived-data maintenance handlers (--fix-zone,
--regen-collision, --build-woc) out of main.cpp into a new
cli_zone_data.{hpp,cpp} module. Distinct from cli_repair
(which fixes manifest-vs-disk drift): these rebuild derived
terrain data and clean up JSON files via load+save:
- fix-zone re-parses + re-saves every JSON sidecar to apply
  load-time scrubs and save-time caps
- regen-collision rebuilds WOC for every WHM/WOT in a zone
- build-woc handles single-tile WOC generation

main.cpp shrinks by 104 lines (1,541 to 1,437).
2026-05-09 09:44:52 -07:00
Kelsi
288c4e93b6 feat(editor): add --gen-texture-runes magical-glyph pattern
44th procedural texture: scattered angular runes drawn as
3-5 random stroke segments per glyph. Each stroke uses one
of 8 cardinal/diagonal angles (0/45/90/135/...°) so the
strokes read as deliberate runic carvings rather than random
scribbles. Layout is a sparse grid with per-slot jitter and
~5% empty slots so the result looks hand-carved rather than
mechanical.

Useful for ancient ruins, magical zones, dwarven walls,
necromancer altars, druidic shrines. Defaults to a 64-px
grid spacing yielding ~15 runes in a 256×256 image.
2026-05-09 09:42:10 -07:00
Kelsi
710fdf9b35 refactor(editor): extract WOM/WOB info handlers into cli_mesh_info.cpp
Moves three mesh-aggregate info handlers (--info-zone-models-total,
--list-zone-meshes-detail, --info-mesh) out of main.cpp into a
new cli_mesh_info.{hpp,cpp} module. The first aggregates
WOM/WOB stats across a zone; the second tabulates per-mesh
metrics sorted by triangle count; the third dumps single-WOM
detail (bounds, version, batches, bones, animations, textures).

main.cpp shrinks by 285 lines (1,826 to 1,541). All three
preserve --json output for capacity-planning pipelines.
2026-05-09 09:40:03 -07:00
Kelsi
993e90f0ee feat(editor): add --gen-mesh-sundial garden-timekeeper primitive
55th procedural mesh: 6-box garden sundial — square base
plate at floor, central vertical gnomon slab spanning the
diameter (long axis along Z, the blade that casts a shadow),
and 4 small hour-marker nubs at the cardinal points (N/S/E/W)
around the rim.

Useful for monastery courtyards, mage tower observatories,
druidic stone circles, manor gardens — anywhere a fantasy
world wants visible time-keeping. Defaults to 0.80m square
base with 0.35m gnomon (~0.41m total height).
2026-05-09 09:37:13 -07:00
Kelsi
f5f4c3d782 refactor(editor): extract simple texture helpers into cli_texture_helpers.cpp
Moves the two basic texture-helper handlers (--gen-texture,
--add-texture-to-zone) out of main.cpp into a new
cli_texture_helpers.{hpp,cpp} module. The first synthesizes
solid hex / checker / grid PNG placeholders; the second
imports an existing PNG into a zone with optional rename and
idempotent re-run support (skip if bytes already match).
Both complement the procedural pattern generators in
cli_gen_texture (which handles the 43 named patterns).

main.cpp shrinks by 196 lines (2,022 to 1,826) and finally
drops below 2K lines.
2026-05-09 09:34:19 -07:00
Kelsi
e117e5aaff refactor(editor): extract CLI introspection handlers into cli_introspect.cpp
Moves five self-discovery handlers (--list-commands,
--info-cli-stats, --info-cli-categories, --info-cli-help,
--gen-completion) out of main.cpp into a new
cli_introspect.{hpp,cpp} module. All five auto-discover
commands by parsing printUsage's stdout via tmpfile capture,
so the surface stays self-describing as new flags are added.

Useful for shell completion scripts that re-exec the binary
at completion time, IDE plugins, and 'is there a flag for X?'
search workflows. main.cpp shrinks by 276 lines (2,298 to
2,022). --validate-cli-help stays inline because it needs
direct access to the static-local kArgRequired array.
2026-05-09 09:31:31 -07:00
Kelsi
96bb1f6d04 feat(editor): add --gen-mesh-scarecrow farm-prop primitive
54th procedural mesh: 5-box cruciform farm pest deterrent —
vertical body post, horizontal cross arm, head box on top of
the body, brimmed hat box that's wider than the head, and a
narrower-but-taller crown box on top of the hat brim. The
cross silhouette + brimmed-hat read as a scarecrow without
needing rotated geometry.

Useful for crop fields, abandoned villages, harvest set
dressing, ritual circles, fields after a battle. Defaults
to 1.80m body × 1.40m arm span (~2.18m total height).
2026-05-09 09:27:56 -07:00
Kelsi
167d523e70 refactor(editor): extract check-* sanity handlers into cli_check.cpp
Moves the four cross-reference / content sanity-check
handlers (--check-zone-refs, --check-zone-content,
--check-project-content, --check-project-refs) out of
main.cpp into a new cli_check.{hpp,cpp} module. Each goes
deeper than --validate (which only checks open-format file
presence) — verifying that quest NPC IDs resolve to
creatures.json entries, model paths resolve to on-disk files,
spawn positions sit inside the zone's tile bounds, etc.

main.cpp shrinks by 509 lines (2,807 to 2,298). All four
preserve --json output for CI pipelines.
2026-05-09 09:25:41 -07:00
Kelsi
02564171b5 refactor(editor): extract for-each batch runners into cli_for_each.cpp
Moves the two batch-runner handlers (--for-each-zone,
--for-each-tile) out of main.cpp into a new
cli_for_each.{hpp,cpp} module. Both substitute `{}` with the
iterated path (find -exec convention) and shell-escape every
token before passing to std::system. Exit code is the failure
count, capped at 255 so the shell can still see it.

main.cpp shrinks by 157 lines (2,964 to 2,807).
2026-05-09 09:23:23 -07:00
Kelsi
4932947631 feat(editor): add --gen-mesh-weathervane rooftop primitive
53rd procedural mesh: 6-box rooftop wind indicator — base
plate, tall vertical post, perpendicular N-S and E-W cross
arms (cardinal direction markers), a long horizontal arrow
on top of the cross, and a small tail box at the back end of
the arrow that visually balances the head and gives the arrow
its directional read.

Useful for farm rooftops, chapel spires, town halls,
lighthouse caps, manor turrets — anywhere a fantasy world
wants visible wind direction. Defaults to a 1.50m post with
0.40m cross arms and a 0.55m-half-length arrow (~1.67m total).
2026-05-09 09:20:08 -07:00
Kelsi
6821549856 refactor(editor): extract dep/orphan handlers into cli_deps.cpp
Moves the three asset-dependency analysis handlers
(--list-zone-deps, --list-project-orphans,
--remove-project-orphans) out of main.cpp into a new
cli_deps.{hpp,cpp} module. All three surface the relationship
between content references (objects.json + WOB doodad lists)
and on-disk model files: list-deps enumerates what a zone
needs, list-orphans flips that into 'what's on disk but
unreferenced', and remove-orphans deletes the resulting set
(with --dry-run for safe previews).

main.cpp shrinks by 355 lines (3,319 to 2,964) and finally
drops below 3K. The shared 'normalize basename' rule for
matching path references stays duplicated between the list
and remove handlers — a deliberate tradeoff, with comments
flagging the sync requirement.
2026-05-09 09:16:26 -07:00
Kelsi
4db41b5974 refactor(editor): extract --info-tilemap into cli_tilemap.cpp
Moves the project-wide ADT-grid visualization handler
(--info-tilemap) out of main.cpp into a new
cli_tilemap.{hpp,cpp} module. Renders the WoW 64x64 tile
grid showing which tiles are claimed by which zones, with
collision detection (multiple zones claiming the same tile)
and per-zone glyph legend. Empty rows are skipped so the
output stays bounded for projects in one map corner.

main.cpp shrinks by 114 lines (3,433 to 3,319). --json
output mode preserved for CI pipelines.
2026-05-09 09:14:01 -07:00
Kelsi
822efff70f feat(editor): add --gen-mesh-beehive (skep) primitive
52nd procedural mesh: woven straw beehive (skep) — 4 stacked
tiers of decreasing width approximating a dome (100% / 90% /
70% / 40% of base width), an optional foundation plate
underneath, and a small entrance notch box on the +Z face
that reads as a cutout rather than texture detail.

Useful for druidic groves, beekeeper farms, hunter camps,
peasant cottages — anywhere a fantasy world wants honey
production. Defaults to 0.70m base × 0.85m tall with a
small 0.05m foundation plate.
2026-05-09 09:11:36 -07:00
Kelsi
db8fa0ec5f refactor(editor): extract --list-zones / --zone-stats into cli_zone_list.cpp
Moves the two zone discovery / aggregation handlers
(--list-zones, --zone-stats) out of main.cpp into a new
cli_zone_list.{hpp,cpp} module. The first quickly enumerates
zones across the standard locations (custom_zones, output);
the second walks a project dir computing tile / creature /
quest / per-format byte totals with a per-zone breakdown
table — useful for content-pack release notes and capacity
planning.

main.cpp shrinks by 191 lines (3,624 to 3,433). Both --json
output modes preserved.
2026-05-09 09:09:06 -07:00
Kelsi
47b4501767 feat(editor): add --gen-texture-cracked branching-walk pattern
43rd procedural texture: organic crack network done via
recursive random walks from N seed nuclei. Each seed spawns
a crack that walks in a random direction for some length,
then with 60% chance branches into one or two more cracks
of half-remaining length. Most cracks die out after a step
or two, a few branch into longer networks — the bias matches
real-world fissure formation.

Useful for cracked mud, dry earth, broken glass, weathered
stone, dragon skin overlays, ice-shard effects. Defaults
to 12 seeds at 40-px max length. Iterative DFS instead of
true recursion so deep branching chains never blow the
stack.
2026-05-09 09:06:56 -07:00
Kelsi
43bb8d133b refactor(editor): extract --gen-makefile / --gen-project-makefile into cli_makefile.cpp
Moves the two Makefile-generation handlers (--gen-makefile,
--gen-project-makefile) out of main.cpp into a new
cli_makefile.{hpp,cpp} module. Both emit GNU make recipes
that rebuild every derived output (.glb / .obj / .stl / .html
/ .csv / .md) from sources via wowee_editor flags, with proper
dependency tracking so make can skip already-up-to-date
outputs. Designers can `make -j$(nproc)` to rebuild all zones
in parallel after a content edit.

main.cpp shrinks by 167 lines (3,791 to 3,624).
2026-05-09 09:04:44 -07:00
Kelsi
bd2d8ffbd0 feat(editor): add --gen-mesh-gate wooden farm-gate primitive
51st procedural mesh: 5-box wooden gate — 2 vertical posts on
either side and 3 horizontal cross rails (top, middle, bottom)
spanning the opening. The gate sits flat in the X-Y plane
(rails along X, posts along Y) so it can hang in a wall slot
or fence gap without needing rotation.

Useful for fenced fields, manor entrances, pen openings,
courtyard barriers, dungeon archway closures. Defaults to a
1.80m opening with 1.30m-tall posts. Pairs with
--gen-mesh-fence for complete enclosures, exporting through
the open-format ecosystem (WOM -> OBJ / GLB / STL) so designers
can preview the gate-to-fence joinery in any external tool.
2026-05-09 09:02:04 -07:00