53rd procedural texture: N rays radiating from the texture
center. Each pixel computes its angle via atan2 and finds
the angular distance to the nearest ray axis (handling the
±π wrap so rays around the seam are continuous). Pixels
inside any ray's angular band get the ray color.
Brightness tapers linearly with distance from center: 1.0
at the hub, 0.4 at the texture diagonal — gives sun-rays
that fade as they extend outward instead of reading as
infinite lines.
Useful for sun motifs, holy/divine symbols, paladin
insignias, mage-robe trim, mosaic medallion centers,
shrine floor inlays, and any "radiant glory" surface.
Sibling of --bake-wom-collision for buildings. Walks every
group in a WOB, optionally welds vertices PER GROUP (groups
are intentionally separate — rooms with portals between them,
so welding across groups would fuse walls that should remain
distinct collision surfaces), and appends each to a single
WoweeCollision via WoweeCollisionBuilder::addMesh.
Same flag surface as the WOM variant: optional [out.woc]
output path, --weld <eps> for per-group vertex merge,
--steep <deg> for the walkable/steep slope cutoff.
Smoke tested on a 1-group cube WOB: 8 verts → 12-triangle
WOC with proper bounds (-1,-1,-1)..(1,1,1) and walkable/
steep classification matching the cube face orientations.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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).
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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).
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.
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.
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.
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).
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.
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.
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.
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).
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).
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.
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.
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).
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.
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.
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).
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.
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).
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).
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.
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.
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.
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.
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.