From c10b1b0fb13e3d16d5436d0be69f0b78a74305ba Mon Sep 17 00:00:00 2001 From: Kelsi Date: Sat, 9 May 2026 11:57:38 -0700 Subject: [PATCH] feat(editor): add --gen-mesh-watchpost sentry tower primitive MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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). --- tools/editor/cli_arg_required.cpp | 1 + tools/editor/cli_gen_mesh.cpp | 70 +++++++++++++++++++++++++++++++ tools/editor/cli_help.cpp | 2 + 3 files changed, 73 insertions(+) diff --git a/tools/editor/cli_arg_required.cpp b/tools/editor/cli_arg_required.cpp index cf3a33c1..07cc5b7f 100644 --- a/tools/editor/cli_arg_required.cpp +++ b/tools/editor/cli_arg_required.cpp @@ -53,6 +53,7 @@ const char* const kArgRequired[] = { "--gen-mesh-canopy", "--gen-mesh-haystack", "--gen-mesh-dock", "--gen-mesh-pergola", "--gen-mesh-chimney", "--gen-mesh-bedroll", "--gen-mesh-workbench", "--gen-mesh-crate-stack", + "--gen-mesh-watchpost", "--gen-mesh-table", "--gen-mesh-lamppost", "--gen-mesh-bed", "--gen-mesh-ladder", "--gen-mesh-well", "--gen-mesh-signpost", "--gen-mesh-mailbox", "--gen-mesh-tombstone", "--gen-mesh-crate", diff --git a/tools/editor/cli_gen_mesh.cpp b/tools/editor/cli_gen_mesh.cpp index b7e1a51f..c2ba6c6f 100644 --- a/tools/editor/cli_gen_mesh.cpp +++ b/tools/editor/cli_gen_mesh.cpp @@ -5212,6 +5212,75 @@ int handleTent(int& i, int argc, char** argv) { return 0; } +int handleWatchpost(int& i, int argc, char** argv) { + // Sentry watchpost: tall central pole topped by a wider square + // platform, with optional corner railing posts. Distinct from + // --gen-mesh-tower (round castle tower with battlements) — a + // watchpost is the rough scout/lookout variant. Pairs with + // --gen-mesh-tent / --gen-mesh-firepit for outdoor camps and + // forward outposts. The 64th procedural mesh primitive. + std::string womBase = argv[++i]; + float postH = 3.0f; + float postW = 0.18f; + float platformSize = 0.8f; + float platformT = 0.10f; + float railingH = 0.45f; // 0 → no railing + float railingW = 0.06f; + parseOptFloat(i, argc, argv, postH); + parseOptFloat(i, argc, argv, postW); + parseOptFloat(i, argc, argv, platformSize); + parseOptFloat(i, argc, argv, platformT); + parseOptFloat(i, argc, argv, railingH); + parseOptFloat(i, argc, argv, railingW); + if (postH <= 0 || postW <= 0 || + platformSize <= 0 || platformT <= 0 || + railingH < 0 || railingW <= 0 || + postW * 2 >= platformSize) { + std::fprintf(stderr, + "gen-mesh-watchpost: dims > 0; postW*2 < platformSize\n"); + return 1; + } + stripExt(womBase, ".wom"); + wowee::pipeline::WoweeModel wom; + initWomDefaults(wom, womBase); + // Central pole: square box from y=0 to y=postH. + addFlatBox(wom, 0.0f, postH * 0.5f, 0.0f, + postW * 0.5f, postH * 0.5f, postW * 0.5f); + // Platform slab on top of the pole. + const float platformY = postH + platformT * 0.5f; + const float halfPlat = platformSize * 0.5f; + addFlatBox(wom, 0.0f, platformY, 0.0f, + halfPlat, platformT * 0.5f, halfPlat); + // Optional 4 corner railing posts above the platform. + if (railingH > 0.0f) { + const float railCY = postH + platformT + railingH * 0.5f; + const float halfRail = railingW * 0.5f; + const float railX = halfPlat - halfRail; + addFlatBox(wom, +railX, railCY, +railX, + halfRail, railingH * 0.5f, halfRail); + addFlatBox(wom, -railX, railCY, +railX, + halfRail, railingH * 0.5f, halfRail); + addFlatBox(wom, +railX, railCY, -railX, + halfRail, railingH * 0.5f, halfRail); + addFlatBox(wom, -railX, railCY, -railX, + halfRail, railingH * 0.5f, halfRail); + } + finalizeAsSingleBatch(wom); + float topY = postH + platformT + (railingH > 0 ? railingH : 0.0f); + wom.boundMin = glm::vec3(-halfPlat, 0, -halfPlat); + wom.boundMax = glm::vec3(+halfPlat, topY, +halfPlat); + if (!saveWomOrError(wom, womBase, "gen-mesh-watchpost")) return 1; + std::printf("Wrote %s.wom\n", womBase.c_str()); + std::printf(" post : %.3f tall, %.3f square\n", postH, postW); + std::printf(" platform : %.3f square, %.3f thick\n", + platformSize, platformT); + std::printf(" railing : %s\n", + railingH > 0 ? std::to_string(railingH).c_str() : "(none)"); + std::printf(" vertices : %zu\n", wom.vertices.size()); + std::printf(" triangles : %zu\n", wom.indices.size() / 3); + return 0; +} + int handleCrateStack(int& i, int argc, char** argv) { // Multi-crate stack: an N×M×K arrangement of cube crates with // a small gap between each so they read as discrete shipping @@ -6134,6 +6203,7 @@ constexpr MeshEntry kMeshTable[] = { {"--gen-mesh-bedroll", 1, handleBedroll}, {"--gen-mesh-workbench", 1, handleWorkbench}, {"--gen-mesh-crate-stack", 1, handleCrateStack}, + {"--gen-mesh-watchpost", 1, handleWatchpost}, {"--gen-mesh-table", 1, handleTable}, {"--gen-mesh-lamppost", 1, handleLamppost}, {"--gen-mesh-bed", 1, handleBed}, diff --git a/tools/editor/cli_help.cpp b/tools/editor/cli_help.cpp index b21ce1c7..735930e1 100644 --- a/tools/editor/cli_help.cpp +++ b/tools/editor/cli_help.cpp @@ -244,6 +244,8 @@ void printUsage(const char* argv0) { std::printf(" Workbench: 4-legged top slab with optional vise at +X end and back tool tray (blacksmith / crafter)\n"); std::printf(" --gen-mesh-crate-stack [crateSize] [columns] [rows] [layers] [gap]\n"); std::printf(" Crate stack: N×M×K cube grid with small gap between crates (warehouses, cargo holds, dockyards)\n"); + std::printf(" --gen-mesh-watchpost [postH] [postW] [platformSize] [platformT] [railingH] [railingW]\n"); + std::printf(" Watchpost: tall pole + square platform + 4 corner railing posts (sentry / scout outpost)\n"); std::printf(" --gen-mesh-table [width] [depth] [height] [legThick] [topThick]\n"); std::printf(" Table: flat top slab on 4 corner legs (default 1.6/1.0/0.85/0.10/0.06)\n"); std::printf(" --gen-mesh-lamppost [poleH] [poleT] [baseSize] [lanternSize] [lanternH]\n");