From b74c369efd80c1fb4b3ff6cf7ebb2b58128bac55 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Sat, 9 May 2026 12:59:16 -0700 Subject: [PATCH] feat(editor): add --gen-mesh-hitching-rail multi-post variant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 74th procedural mesh primitive. Long horizontal hitching bar held up by N evenly-spaced vertical posts. Distinct from --gen-mesh-hitching-post (just 2 posts + bar) — this is the longer multi-post variant for taverns, stockyards, racecourse parking, market days, festival hitching lines. Posts auto-spaced from -L/2 to +L/2, inset by postW so the end posts align with the rail tips. Bar spans the full length on top of the post tier. Watertight under weld (verified 90 manifold edges, 0 boundary, 0 non-manifold). Default 4-post / 4 m rail at 1.2 m mount height fits typical mount silhouettes. Milestone: kArgRequired now reaches 400 documented flags. --- tools/editor/cli_arg_required.cpp | 1 + tools/editor/cli_gen_mesh.cpp | 55 +++++++++++++++++++++++++++++++ tools/editor/cli_help.cpp | 2 ++ 3 files changed, 58 insertions(+) diff --git a/tools/editor/cli_arg_required.cpp b/tools/editor/cli_arg_required.cpp index 67fe942b..173a420d 100644 --- a/tools/editor/cli_arg_required.cpp +++ b/tools/editor/cli_arg_required.cpp @@ -58,6 +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-camp-pack", "--gen-blacksmith-pack", "--gen-village-pack", "--gen-temple-pack", "--gen-graveyard-pack", "--gen-mesh-table", "--gen-mesh-lamppost", "--gen-mesh-bed", diff --git a/tools/editor/cli_gen_mesh.cpp b/tools/editor/cli_gen_mesh.cpp index 2660dbc0..5ebd8af7 100644 --- a/tools/editor/cli_gen_mesh.cpp +++ b/tools/editor/cli_gen_mesh.cpp @@ -5161,6 +5161,60 @@ int handleTent(int& i, int argc, char** argv) { 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- + // post (which is just 2 posts + bar) — this is the longer + // multi-post variant for taverns, stockyards, racecourse + // parking, market days. The 74th procedural mesh primitive. + std::string womBase = argv[++i]; + float length = 4.0f; + float height = 1.2f; + int posts = 4; + float postW = 0.10f; + float barT = 0.08f; + parseOptFloat(i, argc, argv, length); + parseOptFloat(i, argc, argv, height); + parseOptInt(i, argc, argv, posts); + parseOptFloat(i, argc, argv, postW); + parseOptFloat(i, argc, argv, barT); + if (length <= 0 || height <= 0 || postW <= 0 || barT <= 0 || + posts < 2 || posts > 64 || + postW * posts >= length || barT >= height) { + std::fprintf(stderr, + "gen-mesh-hitching-rail: dims > 0; posts 2..64; " + "posts must fit within length\n"); + return 1; + } + stripExt(womBase, ".wom"); + wowee::pipeline::WoweeModel wom; + initWomDefaults(wom, womBase); + const float L2 = length * 0.5f; + // Posts evenly spaced from -L2 to +L2, inset by postW so end + // posts align with the rail ends. + const float postCY = (height - barT) * 0.5f; + const float postHY = (height - barT) * 0.5f; + const float availSpan = length - postW; + for (int k = 0; k < posts; ++k) { + float t = static_cast(k) / (posts - 1); + float cx = -availSpan * 0.5f + t * availSpan; + addFlatBox(wom, cx, postCY, 0.0f, + postW * 0.5f, postHY, postW * 0.5f); + } + // Long horizontal rail spanning the full length on top. + addFlatBox(wom, 0.0f, height - barT * 0.5f, 0.0f, + L2, barT * 0.5f, barT * 0.5f); + finalizeAsSingleBatch(wom); + setCenteredBoundsXZ(wom, L2, postW * 0.5f, height); + if (!saveWomOrError(wom, womBase, "gen-mesh-hitching-rail")) return 1; + printWomWrote(womBase); + std::printf(" rail : %.3fL at H=%.3f, %d posts (W=%.3f)\n", + length, height, posts, postW); + std::printf(" bar : %.3f thick\n", barT); + printWomMeshStats(wom); + return 0; +} + int handleMineCart(int& i, int argc, char** argv) { // Mine cart: rectangular open-top bin on 4 wheel boxes. The // bin uses the 5-piece basin construction from @@ -6920,6 +6974,7 @@ constexpr MeshEntry kMeshTable[] = { {"--gen-mesh-gravel-pile", 1, handleGravelPile}, {"--gen-mesh-stone-bench", 1, handleStoneBench}, {"--gen-mesh-mine-cart", 1, handleMineCart}, + {"--gen-mesh-hitching-rail", 1, handleHitchingRail}, {"--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 aa33a561..70c30210 100644 --- a/tools/editor/cli_help.cpp +++ b/tools/editor/cli_help.cpp @@ -284,6 +284,8 @@ void printUsage(const char* argv0) { std::printf(" Stone bench: long seat slab on 2 block supports near the ends (park / temple / ruined city)\n"); std::printf(" --gen-mesh-mine-cart [length] [width] [bodyH] [wallT] [wheelR] [wheelInset]\n"); 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-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");