71st procedural mesh primitive. Hash-distributed pile of
stone cubes in a roughly conical heap. Each stone gets:
• polar position (radial, theta) with sqrt(rand) on radial
so stones aren't bunched at center
• height limited by yMax = pileH * (1 - radial/baseR), so
larger / more numerous stones land near the base and
smaller ones perch on top — natural gravel-pile profile
• size in the 40-100% range of maxStoneSize
The second multi-box "scene" composite primitive (after
--gen-mesh-crate-stack), but using irregular hashed
placement instead of a regular N×M×K grid. Deterministic
from seed: re-running with same args reproduces the
identical pile.
Useful for mine entrances, construction sites, quarries,
ruined walls, abandoned-fort rubble, pirate-cove stash
mounds. Default 24 stones at 0.6 m base radius gives a
reasonable medium-pile.
Uses every shared helper introduced this batch (printWomWrote,
printWomMeshStats, setCenteredBoundsXZ, addFlatBox,
saveWomOrError, parseOpt*, stripExt).
Two more print-pattern extractions for the gen-mesh side:
• printWomWrote(base) — the "Wrote <base>.wom" success line
every handler emits at the start of its stat report. 71
sites collapsed.
• printWomMeshStats(wom) — the canonical final 2-line pair
"vertices : N" / "triangles : T" emitted at the end of
every standard primitive's stat report. 49 sites collapsed.
Output bytes verified identical: firepit and tent surface
area / vertex / triangle counts unchanged.
The one remaining open-coded "Wrote" line is in
--gen-mesh-textured which writes both a .wom and a .png in
the same line — different signature, left unchanged.
These complete the print-helper trio (printPngWrote on the
texture side, printWomWrote + printWomMeshStats on the mesh
side). New primitives now end with one helper call instead
of three printfs.
65th procedural texture: 2 sets of parallel "translucent"
bands — one horizontal, one vertical — overlaid on a base.
Each band contributes 0.5 alpha, so where bands cross both
half-alphas combine for the darkest color (the unmistakable
plaid grid intersection).
Distinct from --gen-texture-tartan (3-4 colors with
asymmetric stripe pitches) — this is the simple symmetric
2-color variant. Distinct from --gen-texture-checker (hard
binary squares) — plaid uses 3-tone alpha-blend gradient.
Useful for pub-table cloths, country-tavern napery, dwarven
formal-attire trim, picnic blankets, peasant-clothier
swatches. Default 24-stride / 8-band reads as classic
country-checked at 256x256.
First procedural texture to also use the new printPngWrote
helper from this batch.
63 procedural-texture handlers each printed the same two-line
success header at the start of their stat block:
std::printf("Wrote %s\n", outPath.c_str());
std::printf(" size : %dx%d\n", W, H);
The size label had minor whitespace variation (label widths
of 8-15 chars chosen to align with longer per-handler labels).
Hoist into cli_png_emit.hpp as inline printPngWrote(outPath,
W, H) which normalizes to a uniform 11-char label.
Each call site collapses to one line. Output bytes
verified identical via PNG diff: regenerating moss.png with
same args produces byte-for-byte the same file. The visible
text output now uses one consistent label width across all
gen-texture-* commands instead of the previous per-handler
spacing tweaks.
70th procedural mesh primitive. Round-faced archery target on
a 2-post stand:
• face: closed cylinder along the Z axis (flat ±Z caps face
the archer line, side wall is the rim) of radius faceR
centered at height postH
• stand: 2 vertical posts at the bottom, sized to reach
from ground to the bottom of the face, with a horizontal
cross-beam joining them just below the face for rigidity
Concentric scoring rings live in texture space, not geometry —
the cap discs are the natural canvas for a separate target-
ring texture (or use --gen-texture-rings / --gen-texture-
starburst on top).
Pairs naturally with --gen-mesh-training-dummy /
--gen-mesh-fence for sparring grounds, training yards,
militia drill squares, mid-summer fair scenes.
Watertight under weld (verified 198 manifold edges, 0
boundary, 0 non-manifold).
64th procedural texture: vertical rust drips on a metal base.
Each streak is a hash-derived (x position, width 1..4 px,
top y, length H/3..H) band that fades from full rust at the
top to bg at the bottom — the "streak" effect of years of
rain washing rust down from rivets and seams.
Distinct from --gen-texture-rust (generic surface noise) —
this is the directional-drip variant for:
• weathered metal walls (especially under high windows)
• sewer-grate backings under standing puddles
• ship-hull stains below scupper ports
• abandoned-machinery panels under leaking pipes
Deterministic from seed: re-running with same args produces
the byte-identical image. Default 40 streaks at 256x256
gives a reasonable "lightly aged" intensity; bump
streakCount for heavier corrosion.
Two changes in one commit because they're co-designed:
• Extract emitMeshPack(outDir, packName, items) as a shared
helper that takes a vector of PackItem {flag, fn, leaf}
and runs each handler with a synthetic argv. Keeps the
"build a 2-element argv per sub-handler" wiring in one
place instead of copy-pasted per pack.
• Add --gen-blacksmith-pack on top: a smithy-themed
composite emitting forge + anvil + workbench +
water-trough + crate-stack + hitching-post into outDir.
The classic forge-floor layout in 6 .wom files.
Refactors --gen-camp-pack to use emitMeshPack (verified
unchanged: 6 .wom files still land in the camp dir).
Pattern is now established for future packs (--gen-village-
pack, --gen-temple-pack, --gen-dock-pack…) — each is just a
list of primitives in 8 lines of code.
69th procedural mesh primitive. Built from up to 3 axis-
aligned boxes:
• stone hearth: rectangular base spanning width × depth ×
baseH, where the smith's fire would sit
• hood: smaller smoke-collector box on top of the hearth,
inset by hoodInset on each side so it reads as the
chimney's flared base
• optional thin chimney rising from the hood — set
chimneyH=0 for an open-vented forge
Pairs naturally with --gen-mesh-anvil and
--gen-mesh-workbench for blacksmith/armorer/farrier scenes,
and with --gen-mesh-bench / --gen-mesh-crate-stack for
the storage corner of a smithy.
Watertight under weld (verified 54 manifold edges, 0
boundary, 0 non-manifold). Default 1.4×1.0×0.9 m hearth +
0.5 m hood + 1.2 m chimney reads as a normal village smithy.
63rd procedural texture: minor + major grid lines on a base.
Minor lines at every `minorStride` pixels (default 16);
every `majorEvery` minor lines a thicker major line is drawn
(default every 4). Both axes get the same treatment so the
result is a sectioned drafting / engineer's paper look.
Distinct from --gen-texture-mesh-screen (uniform line
weight) — this is the periodic-emphasis variant. Good for:
• drafting-table surfaces
• technical-schematic backdrops
• architect-shop / cartographer-shop walls
• engineer / scholar ritual tables
• star-chart room floors
Default 16-stride / every-4-major gives the classic blue
graph-paper aesthetic at 256x256. First procedural texture
that takes 4 numeric layout params plus W/H — exercises the
new parseOpt* helpers across more args than usual.
Filtered subset of --list-commands: just procedural primitive
flags (--gen-mesh-* and --gen-texture-*). Walks the shared
kArgRequired registry so it auto-tracks new primitives as they
land — no parallel list to maintain. Useful when authoring
content packs to discover what's available without scrolling
through the full --help dump.
Flags:
--mesh — show only --gen-mesh-* (default: both)
--texture — show only --gen-texture-* (default: both)
--json — JSON output with meshCount/textureCount totals
Currently surfaces 70 procedural mesh primitives and 62
procedural texture primitives. JSON form is well-suited for
piping into sortable / searchable UIs (e.g. content-pack
authoring tools).
68th procedural mesh primitive. Built from 3 axis-aligned
boxes:
• solid body slab spanning width × depth × (height-roofT)
• thin door slab on the +Z face (pushed slightly outward
so it visually sits on the wall — gives doorframe-like
relief without a real cutout)
• roof slab slightly larger than the body footprint and
sitting on top, controlled by roofOverhang
Distinct from --gen-mesh-house (multi-walled, peaked-roof
dwelling). An outhouse is the single-room privy / tool shed
variant — useful for villages, frontier outposts, small
farms, refuse-shed corners.
Watertight under weld (verified 54 manifold edges, 0
boundary, 0 non-manifold). First handler to use the new
setCenteredBoundsXZ helper from this batch.
Convenience composite command: emits a complete outdoor-camp
scene (tent + firepit + bedroll + canopy + woodpile +
haystack) into <outDir>/ as 6 .wom files in one invocation.
Internally builds a synthetic argv array per primitive and
calls each existing handler — so the camp pack always
matches the per-primitive defaults exactly. Users wanting
custom dimensions should call the individual --gen-mesh-*
commands.
Smoke tested:
• Creates outDir if needed
• All 6 .wom files validate clean (--validate-wom PASSED)
• Vertex / triangle counts match per-primitive defaults
(firepit 240/120, woodpile 324/288, etc.)
The first composite command in the editor — establishes the
pattern for future packs (--gen-village-pack, --gen-dock-pack,
etc.) by showing how synthetic argv arrays let one handler
delegate to many others without spawning subprocesses.
62nd procedural texture: vertical bamboo stalks with
cylindrical sin² brightness shading + horizontal darker
node bands at regular Y intervals. Each stalk gets a small
hash-derived hue jitter (±15) so adjacent stalks don't
read as a perfect repeat.
Per-pixel logic:
• 10% horizontal gap between stalks (gapFrac=0.10) →
visible bg seam between bamboo stalks
• Inside the stalk, brightness = sin²(insetFraction * π)
rising from 0 at the stalk edge to 1 at the centerline
• Every nodeY pixels, a `nodeBand`-tall horizontal strip
has its brightness knocked down to 2/3 — the segmented
bamboo joint look
Useful for jungle huts, pandaren / asian-themed architecture,
swamp boardwalks, tropical-tribe weapon trim, brewmaster
zone trim. Default 24-pitch / 64-spacing / 4-band reads as
adult-bamboo at typical 256x256 wall-tile scale.
21 procedural mesh handlers used the same two-line bounds
stanza for primitives whose footprint is symmetric in X+Z
and rises from y=0:
wom.boundMin = glm::vec3(-halfX, 0, -halfZ);
wom.boundMax = glm::vec3( halfX, maxY, halfZ);
Hoist into cli_box_emitter.hpp as inline setCenteredBoundsXZ
(WoweeModel&, halfX, halfZ, maxY). Each call site collapses
to one line.
Output bytes verified identical: firepit bbox 1.200 × 0.200
× 1.200 unchanged, haystack bbox 1.200 × 0.900 × 1.200
unchanged. Asymmetric bounds (e.g. handlePodium with
different X and Z extents on the min/max lines) are
deliberately left untouched — the helper only matches the
common symmetric case.
67th procedural mesh primitive. Standard town/stable
hitching post:
• two vertical posts separated by `span`
• horizontal cross-bar at upper-post height (length =
span - postW so it tucks INSIDE the post inner faces,
matching real fence joinery)
• optional decorative caps on each post — set capH=0
for a bare working-yard post
All axis-aligned boxes, exercises every shared helper
(stripExt, initWomDefaults, addFlatBox,
finalizeAsSingleBatch, saveWomOrError). Watertight under
weld (verified 90 manifold edges, 0 boundary, 0
non-manifold).
Useful for stables, taverns with mount parking, town
squares, frontier outposts, ranger camps, post-and-rail
fence segments.
61st procedural texture: thin horizontal + vertical wires
forming an axis-aligned grid. Distinct from
--gen-texture-lattice (which uses ±45° diagonals to make
diamond openings) — this gives the right-angle window-
screen / chain-link / sci-fi-grille look.
Tested per-axis: a row is "on a wire" if (y % stride) <
wireW; a pixel falls on a wire if either its row OR its
column tests positive. Result is the unmistakable
orthogonal-mesh appearance.
Useful for window screens, anti-pest grates, fish nets,
sewer grilles, sci-fi vent panels, prison-cell windows,
goblin-workshop catwalks. Default 12-stride / 2-wide
gives a coarse mesh; finer versions read as bug screen.
29 procedural-texture inner loops in cli_gen_texture.cpp
open-coded the same 4-line "compute index, write 3 RGB
bytes" block:
size_t idx = (static_cast<size_t>(y) * W + x) * 3;
pixels[idx + 0] = r;
pixels[idx + 1] = g;
pixels[idx + 2] = b;
Hoist into cli_png_emit.hpp as inline setPixelRGB(pixels,
W, x, y, r, g, b). Header-inline because the procedural
handlers call this once per pixel — the abstraction must
not cost a function-call frame per write.
Each call site collapses to 1 line. cli_gen_texture.cpp
loses ~90 lines from inner-loop bodies. Output bytes
verified identical via PNG diff for moss / camo / snake-skin
— byte-for-byte the same files as previously generated.
(One frost-blend site keeps the open-coded form because
it does a read-modify-write alpha blend rather than a
pure write.)
66th procedural mesh primitive. Combat training dummy from
axis-aligned boxes:
• vertical post from ground to baseH (the stand)
• cubic torso block stacked on top of the post
• horizontal cross-bar arms at upper-third torso height
spanning armSpan along the X axis
• optional head cube above the torso (set headSize=0
for a headless training-block style)
Pairs with --gen-mesh-anvil / --gen-mesh-workbench /
--gen-mesh-fence for sparring grounds, militia drill
squares, training yards, weapon-master compounds. Useful
for any "things-to-hit" set dressing where animated NPCs
would target a static prop.
Watertight under weld (verified 72 manifold edges, 0
boundary, 0 non-manifold). Default 1.0 m post + 0.4 m
torso + 0.18 m head reads at adult-human height.
60th procedural texture: brick-offset grid of diamond-shaped
scales using L1 (taxicab) metric so |dx|/halfW + |dy|/halfH
< 1 inside each diamond. Even rows are aligned with the cell
grid; odd rows shift by halfW so adjacent scales touch
tangentially along their points — the classic snake/dragon
hide pattern.
A thin dark outline at d ∈ [outlineFrac, 1] gives definition;
outline color is automatically derived as scaleHex × 0.4
(2/5 brightness). Set outlineW=0 to skip the outline for a
smoother painted-scale look.
Distinct from --gen-texture-scales (overlapping circles for
fish/dragon scales) and --gen-texture-chainmail (rings for
metal mail). Useful for snake-cult robe trim, naga skin,
dragon-scale armor inserts, lizardman set dressing,
basilisk-themed dungeon decor.
parseHex was a static helper in cli_gen_texture.cpp. 81 sites
across the file each open-coded the same 6-line "if !parseHex
then fprintf 'X is not a valid hex color' and return 1" block.
Hoist parseHex into cli_png_emit.hpp as inline (header-only,
non-trivial but only called once per handler at startup so
inlining cost is negligible). Add inline parseHexOrError that
wraps it with the canonical error message.
Each call site collapses from 6 lines to 1:
if (!parseHexOrError(stoneHex, sr, sg, sb,
"gen-texture-cobble")) return 1;
Bonus: 29 compound-color sites that used a single
"one of the hex colors is invalid" message are split into
separate parseHexOrError calls per color — now the error
identifies WHICH color failed instead of just saying
"one of them".
cli_gen_texture.cpp drops by ~320 lines (4877 → 4557). Output
bytes verified identical for cobble / mosaic / stained-glass.
Error message contract improved: 'NOTHEX' now reports
"'NOTHEX' is not a valid hex color" everywhere instead of
the generic compound message.
65th procedural mesh primitive. Open-top rectangular basin
built from 5 axis-aligned boxes (uses every shared helper
from cli_box_emitter):
• bottom slab spanning full footprint at wallT thickness
• +X / -X side walls spanning full length
• +Z / -Z front/back walls spanning the inner length so
they tuck inside the side walls without overlap
Inner cavity (length-2*wallT × height-wallT × width-2*wallT)
is the open water/feed volume. Useful for stables, farmsteads,
taverns, stockyards, alchemist mixing basins.
Default 1.4 × 0.5 × 0.5 m is a reasonable horse-trough size;
configurable for smaller hand-basins or larger livestock
troughs.
59th procedural texture: 2-octave bilinear value noise
thresholded into hard bg/fg blobs. Sharp edges read as
real military camouflage rather than the smooth gradient
--gen-texture-noise-color produces.
Two octaves combined at 0.7/0.3 weights:
• low frequency at cellSize lattice → large dominant blobs
• high frequency at cellSize/4 lattice → finer mottling
overlay so blob edges aren't perfectly smooth
Threshold parameter shifts the bg/fg balance — lower values
expose more accent color (good for leaf-pattern), higher
values keep the dominant color dominant (good for rock/sand).
Useful for guard cloaks, military uniforms, ghillie suits,
druid robes, hunter blinds, scout cloaks. First procedural
texture to use the new savePngOrError helper from
cli_png_emit.hpp.
58 procedural-texture handlers in cli_gen_texture.cpp ended
with the same 6-line stbi_write_png + error-handling block:
if (!stbi_write_png(outPath.c_str(), W, H, 3,
pixels.data(), W * 3)) {
std::fprintf(stderr,
"gen-texture-foo: stbi_write_png failed for %s\n",
outPath.c_str());
return 1;
}
Hoist into cli_png_emit.hpp as inline savePngOrError, used as
`if (!savePngOrError(outPath, W, H, pixels, "cmd")) return 1;`.
cli_gen_texture.cpp drops by ~118 lines. Output bytes verified
identical via diff: regenerating moss.png with same args
produces byte-for-byte the same file.
This is the texture-side counterpart to saveWomOrError in
cli_box_emitter.hpp — same write-check-fprintf pattern, same
1-line API.
64th procedural mesh primitive. A simple scout/sentry
watchpost from axis-aligned boxes:
• tall central pole rising from ground to postH
• square platform slab on top of the pole, wider than
the pole so it overhangs as the lookout deck
• 4 corner railing posts on the platform — set
railingH=0 for a bare deck
Distinct from --gen-mesh-tower (round castle tower with
crenellated battlements). A watchpost is the rough scout/
forward-outpost variant — just wood and rope, no masonry.
Pairs naturally with --gen-mesh-tent / --gen-mesh-firepit
for camp + lookout scenes, and --gen-mesh-fence for
perimeter setups. Watertight under weld (verified
108 manifold edges, 0 boundary, 0 non-manifold).
58th procedural texture: thin vertical lines at every
`stride` x position, with optional thicker "feature" line
every Nth stripe. The periodic doubled-thickness emphasis
is what distinguishes this from --gen-texture-stripes
(which renders wide alternating bands of each color).
Each stripe is centered within its column and has
configurable lineW; feature stripes are 2× the regular
width. Set featureEvery=0 to disable feature emphasis for
a uniform pinstripe pattern.
Useful for noble-house tabards, formal-attire fabric,
banker's counter cloth, sci-fi panel detailing, royal-
guard uniform trim, library / archive carpet runners.
Default 12-pixel stride, 1-pixel line, feature every 6th
stripe gives the classic suit-pinstripe look at 256x256.
Two more boilerplate patterns repeated across cli_gen_mesh.cpp:
• init: 64 sites set wom.name = path-stem and wom.version = 3
in 3 lines. Hoisted to initWomDefaults(wom, base) — 1 line.
• save: 65 sites had identical 5-line "if save fails, fprintf
stderr and return 1" blocks. Hoisted to saveWomOrError(wom,
base, cmdName) returning bool, used as
`if (!saveWomOrError(...)) return 1;` — also 1 line.
Net cli_gen_mesh.cpp drops by ~254 lines (from 6424 to 6170).
Output bytes verified identical: firepit surface area 2.1100
m² unchanged, cube vertex/index/bounds counts unchanged.
After this batch's prior helpers (addFlatBox, addVertex,
stripExt, finalizeAsSingleBatch, parseOpt*), a typical new
gen-mesh handler now needs zero copy-paste boilerplate to
get a working primitive — every shared bit lives in
cli_box_emitter.hpp and cli_arg_parse.hpp.
63rd procedural mesh primitive. The first to explicitly
compose a *scene* of multiple objects rather than a single
structure: an N×M×K arrangement of cube crates with a small
gap between each so they read as discrete shipping boxes
rather than one merged solid.
Default 2×2×2 = 8 crates with 0.40 m sides on a 0.02 m gap
gives a tight pyramidal pile. Larger configurations (e.g.
4×3×2 = 24 crates) read as warehouse pallets.
Uses every shared helper added this batch — addFlatBox for
each crate, finalizeAsSingleBatch for the trailing batch,
stripExt for the .wom suffix, parseOptInt/Float for the
optional dimension args. Each crate emits the standard
24-vert / 12-tri box, so an N×M×K stack is a clean
N*M*K-multiple in vertices and triangles.
Useful for warehouses, cargo holds, dock loading bays,
market stalls, dwarven mining caches, pirate stash piles.
Watertight under weld — the gap keeps adjacent crates
from sharing corner positions.
53 procedural mesh primitives ended with the same 5-line
batch-finalization boilerplate:
wowee::pipeline::WoweeModel::Batch batch;
batch.indexStart = 0;
batch.indexCount = static_cast<uint32_t>(wom.indices.size());
batch.textureIndex = 0;
wom.batches.push_back(batch);
Hoist into cli_box_emitter.hpp as inline finalizeAsSingleBatch
(WoweeModel&). Each call site collapses to:
finalizeAsSingleBatch(wom);
cli_gen_mesh.cpp drops by ~210 lines. Output bytes verified
identical: firepit batch idx 0 / iStart 0 / iCount 360 /
opaque blend, surface area 2.1100 m² unchanged.
Procedural builders only ever emit one batch per primitive;
multi-batch primitives (none currently exist) would still
be free to construct batches manually.
57th procedural texture: 2x2 cell pattern where alternating
cells hold horizontal vs vertical fiber segments. Each
segment gets a sin² brightness profile across its
perpendicular axis — 0 at the segment edge, peak at center —
giving the rounded highlight of a real woven fiber bundle.
Cell-pair parity (cx + cy) % 2 determines orientation, so
adjacent cells always alternate weave direction in both X
and Y. Reads as the unmistakable carbon-fiber checkerboard
even at small scales.
Useful for sci-fi armor, sleek tech panels, vehicle bodies,
ritual obsidian inlays, dwarven runeplate accents — any
"machined composite" surface where flat dark grey would
read as plain plastic.
Default 12-pixel cell at 256x256 reads cleanly with about
21 visible weave segments per side.
64 sites in cli_gen_mesh.cpp open-coded the same 4-line
"strip .wom suffix from base path if user typed it" pattern:
if (womBase.size() >= 4 &&
womBase.substr(womBase.size() - 4) == ".wom") {
womBase = womBase.substr(0, womBase.size() - 4);
}
Hoist into cli_box_emitter.hpp as inline stripExt(string&,
const char*). Generic on the suffix string so future callers
can use it for ".wob" / ".woc" / ".png" too — uses
string::compare instead of substr to avoid the temporary
allocation the original did per check.
Each call site collapses to a single line:
stripExt(womBase, ".wom");
cli_gen_mesh.cpp drops by ~96 lines. Output bytes verified
identical: firepit surface area 2.1100 m² unchanged.
62nd procedural mesh primitive. A blacksmith / crafter
workbench built from axis-aligned boxes (uses the new
cli_box_emitter helper plus the new cli_arg_parse helpers
for all 7 optional dimension args):
• flat top slab spanning length × depth at topT thickness
• 4 corner legs sized so their outer faces line up with
the top edges (post-style, no overhang)
• optional vise box at the +X end of the top — set
viseSize=0 for a bare bench
• optional raised tool tray along the +Z back edge —
set trayH=0 to omit
Pairs naturally with --gen-mesh-anvil (existing) for
blacksmith forge scenes, and --gen-mesh-cauldron for
alchemist labs. Default 1.6×0.7×0.85 m bench reads at
realistic adult-human working height.
Watertight under weld (verified via --info-mesh-stats:
126 manifold edges, 0 boundary, 0 non-manifold).
56th procedural texture: concentric annual growth rings
centered slightly off-image (at -W*0.2, H/2) so the texture
shows sweeping arcs across most of its area rather than the
bullseye --gen-texture-rings produces.
Per-ring jitter shifts the dark-band center within each
ring's annular cell, so adjacent rings don't read as a
perfect modulus pattern — mimics real annual-growth
variation. Brightness lerps from lightHex (early-wood)
to darkHex (late-wood / heartwood) with a smooth
triangular falloff at the dark-band peak.
First procedural texture to use the new cli_arg_parse
helpers for the optional spacing/seed/W/H args.
Useful for tabletops, log-end caps (chopped firewood
stumps), barrel lids, beam cross-sections, plank ends —
distinct from --gen-texture-wood which renders vertical
grain streaks for plank surfaces.
Every --gen-texture-* and --gen-mesh-* handler had its own
copy of the same 3-line "if there's another arg AND it
doesn't look like a switch, parse it; otherwise keep the
default" block. 458 sites across cli_gen_texture.cpp and
cli_gen_mesh.cpp duplicated this pattern.
Hoist into cli_arg_parse.hpp as inline parseOpt{Int,Float,Uint}
(int& i, int argc, char** argv, T& value). Each call site
collapses from 3 lines to 1:
if (i + 1 < argc && argv[i + 1][0] != '-') {
try { width = std::stof(argv[++i]); } catch (...) {}
}
becomes
parseOptFloat(i, argc, argv, width);
cli_gen_mesh.cpp drops by ~250 lines, cli_gen_texture.cpp
by ~430 lines. Output bytes verified identical: firepit
default-arg surface area 2.1100 m² unchanged.
Future texture/mesh primitives now opt in by including one
header instead of pasting the lambda.
61st procedural mesh primitive. Builds a bedroll prop:
• horizontal closed cylinder along the Z axis sitting at
y = R so it rests on the ground (radius defaults to
0.16 m, length 1.4 m — adult-human-shaped)
• optional pillow box at the +Z end (squashed cube,
pillowSize controls extent)
• per-segment N-sided cylinder uses the new addVertex
helper; pillow uses addFlatBox
Set pillowSize=0 for a bare rolled mat. Watertight under
weld (verified via --info-mesh-stats: 90 manifold edges,
0 boundary, 0 non-manifold).
Pairs naturally with --gen-mesh-tent / --gen-mesh-firepit /
--gen-mesh-canopy for outdoor camp scenes — completes the
tent-side camping kit.
55th procedural texture: irregular spots scattered on a hash-
jittered grid. Each cell tests its 8 neighbors so spots near
cell boundaries don't get clipped at the cell wall.
Per-cell hashing controls:
• presence — density 0-100 chance the cell hosts a spot
• position — uniform jitter inside the cell
• radius — 25-75 % of stride, so adjacent spots vary in size
Result reads as random patches of organic growth rather than
a visible lattice. Useful for forest floors, weathered stone
walls, dungeon flagstones, swamp ground, ruined-temple
overlay, fungal-cavern detail.
Default 16-stride / 70-density on a stone-grey base reads
cleanly at 256x256 with about half the surface mossed over.
Walks every .wom and .wob under <zoneDir>, optionally welds
each one independently (per-mesh / per-WOB-group), and
appends its triangles to a single WoweeCollision. Useful for
shipping a zone — one .woc artifact holds all object collision
so the server side has just one file to load.
Per-file weld preserves between-object boundaries: two
distinct WOMs sitting at the same world position keep
their topology separate even if their corner positions
happen to overlap.
Same flag surface as the single-file bakers: optional
[out.woc] (defaults to <zoneDir>/zone.woc), --weld <eps>,
--steep <deg> for the walkable/steep slope cutoff.
Smoke tested over /tmp/migtest: scanned 79 WOM + 1 WOB,
produced 9259-triangle WOC (1563 walkable, 1943 steep)
with correct world bounds.
60th procedural mesh primitive. Builds a chimney as two
axis-aligned boxes (uses the shared addFlatBox helper):
• main shaft: rectangular brick stack of width × depth ×
(height - capH)
• protective cap: a slightly wider box on top (capExtra
wider than shaft on every side) — the rain-throw crown
that sits on real masonry chimneys
Set capH=0 for a bare unfinished stack. Default 0.45×0.45×
1.8 m shaft with a 0.10-tall cap reads at human scale for
rooftop placement.
Useful for cottage rooflines, blacksmith forges, alchemist
labs, dwarven longhouses — any structure with a fireplace
that needs visible vent. Watertight under weld (verified
via --info-mesh-stats: 36 manifold edges, 0 boundary, 0
non-manifold).
Sibling of --audit-watertight that walks every .wob under
<root> and runs the welded watertight check on every group.
A WOB passes only if every group is closed — interior rooms
in a real building should each be a closed solid even
though the building as a whole has intentional portal
openings between them.
Per-failure detail lists which groups failed and why
(boundary edge count + non-manifold edge count). Exit code
is the number of failed buildings (capped at 255) — same
CI-friendly contract as the WOM audit.
Smoke tested against /tmp/migtest cube.wob: PASS, 12 tris,
exit code 0.
54th procedural texture: grid of round rivet caps with an
inner highlight. Each stud has:
• outer ring (40%-100% of studR) at the studHex base color
• inner core (0-40% of studR) at studHex × 1.4 brightness
• bg color outside studR
The 40% bright core sells the 3D rivet appearance — a flat
solid circle (which --gen-texture-dots already produces)
reads as a polka dot, while the bright/dim concentric pair
reads as a metal stud catching light.
Useful for studded leather armor, riveted plate, banded
mail trim, dwarven hauberks, prison-cell door reinforcement,
ship hull plating. Default 24-stride grid with R=7 reads
cleanly at typical body-armor scale.
After the addBox extraction, 21 procedural mesh primitives
each still open-coded the same 5-line addV vertex-emit lambda.
Add inline addVertex(WoweeModel&, vec3, vec3, vec2) to
cli_box_emitter.hpp (header so the per-vertex hot loop stays
inlineable), plus a per-float overload for the four handlers
(--gen-mesh-stairs, --gen-mesh-tube, --gen-mesh-capsule,
--gen-mesh-arch) that compute pos/normal/uv components inline
rather than building intermediate glm vectors.
Each lambda site collapses from a 5-line body to a 3-line
forwarding wrapper. Output bytes verified identical via
--info-mesh-stats: firepit surface area 2.1100 m² unchanged
across all variants tested (stairs/tube/capsule/arch/
firepit/mushroom).
Sets up the pattern so the next "shared mesh helper" is one
line of include, not another paste-in.
59th procedural mesh primitive. Builds an open-top pergola
from axis-aligned boxes (uses the new addFlatBox helper):
• 4 corner posts sized so their outer faces line up with
the footprint perimeter
• 2 long perimeter beams resting on top of the posts
along the long edges
• N cross beams running between the perimeter beams
(slightly thinner so the lattice has visible crossings)
Distinct from --gen-mesh-canopy because there's no closed
overhead panel — the open lattice top reads as a sun-trellis
or vine-arbor instead of a market-stall awning. Useful for
palace courtyards, druid groves, garden mazes, viewing
arbors, harvest-festival entrance pieces.
Set crossbeams=0 for a bare 4-post + 2-beam frame; default
5 crossbeams gives a pleasing alternating pattern.
36 procedural mesh primitives in cli_gen_mesh.cpp each open-
coded the same ~30-line lambda for emitting a flat-shaded
axis-aligned box (per-face vertices for unique normals,
6 faces × 4 verts × 2 tris). Hoist the implementation into
cli_box_emitter.{hpp,cpp} as addFlatBox(WoweeModel&, ...)
with two overloads:
• addFlatBox(wom, cx, cy, cz, hx, hy, hz)
center + half-extents form, used by 34 primitives
• addFlatBox(wom, glm::vec3 lo, glm::vec3 hi)
lo/hi corners form, used by --gen-mesh-archway and
--gen-mesh-fence which compute corners directly
Each lambda site collapses from ~30 lines to a 3-line thin
wrapper: cli_gen_mesh.cpp drops from 7374 to 6989 lines.
Output bytes verified identical via --info-mesh-stats
(firepit surface area 2.1100 m² unchanged, vertex/triangle
counts match across firepit/canopy/dock/archway/fence).
Future box-based primitives now opt in by including one
header instead of pasting the lambda again.
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.