From 074f299c8b420e8d425860f4d4eda5094908949f Mon Sep 17 00:00:00 2001 From: Kelsi Date: Sat, 9 May 2026 13:04:55 -0700 Subject: [PATCH] feat(editor): add --gen-mesh-pillar-row colonnade primitive MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 75th procedural mesh primitive. Row of N stone pillars evenly spaced along the X axis. Each pillar is a single tall rectangular box optionally crowned by a slightly-wider square cap (capExtra wider per side). End pillars inset by pillarW so they sit at exactly the ±span/2 ends. Set capH=0 for bare un-crowned columns. Useful for ruined temples, palace colonnades, dungeon hallways, dwarven mead-hall arcades, ceremonial entry walks. Distinct from --gen-mesh-pillar (single column) — this is the regular-multi-column composite, the third "scene" primitive after --gen-mesh-crate-stack and --gen-mesh-gravel-pile. Watertight under weld (verified 144 manifold edges, 0 boundary, 0 non-manifold). Default 4-pillar / 4 m / 2.5 m spacing reads as a small temple front porch. --- tools/editor/cli_arg_required.cpp | 2 +- tools/editor/cli_gen_mesh.cpp | 62 +++++++++++++++++++++++++++++++ tools/editor/cli_help.cpp | 2 + 3 files changed, 65 insertions(+), 1 deletion(-) diff --git a/tools/editor/cli_arg_required.cpp b/tools/editor/cli_arg_required.cpp index a6e7cca5..a6032c15 100644 --- a/tools/editor/cli_arg_required.cpp +++ b/tools/editor/cli_arg_required.cpp @@ -58,7 +58,7 @@ const char* const kArgRequired[] = { "--gen-mesh-outhouse", "--gen-mesh-forge", "--gen-mesh-archery-target", "--gen-mesh-gravel-pile", "--gen-mesh-stone-bench", "--gen-mesh-mine-cart", - "--gen-mesh-hitching-rail", + "--gen-mesh-hitching-rail", "--gen-mesh-pillar-row", "--gen-camp-pack", "--gen-blacksmith-pack", "--gen-village-pack", "--gen-temple-pack", "--gen-graveyard-pack", "--gen-garden-pack", diff --git a/tools/editor/cli_gen_mesh.cpp b/tools/editor/cli_gen_mesh.cpp index 96113114..de371141 100644 --- a/tools/editor/cli_gen_mesh.cpp +++ b/tools/editor/cli_gen_mesh.cpp @@ -5161,6 +5161,67 @@ int handleTent(int& i, int argc, char** argv) { return 0; } +int handlePillarRow(int& i, int argc, char** argv) { + // Row of N stone pillars evenly spaced along the X axis. + // Each pillar is a single tall rectangular box, optionally + // crowned by a slightly-wider square cap. Useful for ruined + // temples, colonnades, palace walks, dungeon hallways. The + // 75th procedural mesh primitive — and the third "scene" + // composite (after crate-stack and gravel-pile) using the + // simple regular-grid placement. + std::string womBase = argv[++i]; + int count = 4; + float span = 4.0f; // total length spanned by the row + float height = 2.5f; // pillar height + float pillarW = 0.30f; // pillar square footprint + float capH = 0.10f; // 0 → no caps + float capExtra = 0.06f; // caps wider than pillar by this on each side + parseOptInt(i, argc, argv, count); + parseOptFloat(i, argc, argv, span); + parseOptFloat(i, argc, argv, height); + parseOptFloat(i, argc, argv, pillarW); + parseOptFloat(i, argc, argv, capH); + parseOptFloat(i, argc, argv, capExtra); + if (count < 2 || count > 64 || + span <= 0 || height <= 0 || pillarW <= 0 || + capH < 0 || capExtra < 0 || + pillarW * count >= span) { + std::fprintf(stderr, + "gen-mesh-pillar-row: count 2..64; pillars must fit in span\n"); + return 1; + } + stripExt(womBase, ".wom"); + wowee::pipeline::WoweeModel wom; + initWomDefaults(wom, womBase); + const float availSpan = span - pillarW; + const float pillarHY = (height - capH) * 0.5f; + const float pillarCY = pillarHY; + for (int k = 0; k < count; ++k) { + float t = static_cast(k) / (count - 1); + float cx = -availSpan * 0.5f + t * availSpan; + addFlatBox(wom, cx, pillarCY, 0.0f, + pillarW * 0.5f, pillarHY, pillarW * 0.5f); + if (capH > 0.0f) { + const float capCY = height - capH * 0.5f; + const float capHX = pillarW * 0.5f + capExtra; + addFlatBox(wom, cx, capCY, 0.0f, + capHX, capH * 0.5f, capHX); + } + } + finalizeAsSingleBatch(wom); + float halfX = span * 0.5f + (capH > 0 ? capExtra : 0); + float halfZ = pillarW * 0.5f + (capH > 0 ? capExtra : 0); + setCenteredBoundsXZ(wom, halfX, halfZ, height); + if (!saveWomOrError(wom, womBase, "gen-mesh-pillar-row")) return 1; + printWomWrote(womBase); + std::printf(" pillars : %d across %.3fL, %.3f tall (%.3f square)\n", + count, span, height, pillarW); + std::printf(" caps : %s\n", + capH > 0 ? std::to_string(capH).c_str() : "(none)"); + printWomMeshStats(wom); + return 0; +} + int handleHitchingRail(int& i, int argc, char** argv) { // Long hitching rail: a horizontal bar held up by N evenly- // spaced vertical posts. Distinct from --gen-mesh-hitching- @@ -6994,6 +7055,7 @@ constexpr MeshEntry kMeshTable[] = { {"--gen-mesh-stone-bench", 1, handleStoneBench}, {"--gen-mesh-mine-cart", 1, handleMineCart}, {"--gen-mesh-hitching-rail", 1, handleHitchingRail}, + {"--gen-mesh-pillar-row", 1, handlePillarRow}, {"--gen-camp-pack", 1, handleGenCampPack}, {"--gen-blacksmith-pack", 1, handleGenBlacksmithPack}, {"--gen-village-pack", 1, handleGenVillagePack}, diff --git a/tools/editor/cli_help.cpp b/tools/editor/cli_help.cpp index 5492899b..78f06fc0 100644 --- a/tools/editor/cli_help.cpp +++ b/tools/editor/cli_help.cpp @@ -288,6 +288,8 @@ void printUsage(const char* argv0) { std::printf(" Mine cart: open-top bin (5-piece basin) on 4 wheel boxes (mines / dwarven forges / junk yards)\n"); std::printf(" --gen-mesh-hitching-rail [length] [height] [posts] [postW] [barT]\n"); std::printf(" Hitching rail: long horizontal bar on N evenly-spaced posts (taverns / stockyards / market days)\n"); + std::printf(" --gen-mesh-pillar-row [count] [span] [height] [pillarW] [capH] [capExtra]\n"); + std::printf(" Pillar row: N evenly-spaced rectangular pillars with optional square caps (colonnade / temple ruin)\n"); std::printf(" --gen-camp-pack \n"); std::printf(" Convenience: emit tent + firepit + bedroll + canopy + woodpile + haystack into outDir as 6 .wom files\n"); std::printf(" --gen-blacksmith-pack \n");