From efc27ba7d256c07b7072fe4f71abcd2a0b5c685e Mon Sep 17 00:00:00 2001 From: Kelsi Date: Sat, 9 May 2026 16:21:35 -0700 Subject: [PATCH] feat(editor): add --gen-mesh-rune-stone standing monolith MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 91st procedural mesh: a wide flat base block with a tall narrow monolith standing on top. Reads as a small carved standing-stone marker. Distinct from existing ritual-prop primitives: • --gen-mesh-altar — table-shaped, has flat top • --gen-mesh-shrine — multi-tier with cap • --gen-mesh-tombstone — narrower, curved-top silhouette • --gen-mesh-pillar — round column, no base block • --gen-mesh-statue — has figure on top Useful for: druid groves (boundary markers), witch shrines, ancient ruins (pre-civilization monuments), graveyard boundary stones, faction territory markers, Stonehenge-style ring formations (use 4-8 instances around a center point). 48 verts / 24 tris from two simple boxes — minimal vertex budget, suitable for placing in dense clusters. --- tools/editor/cli_arg_required.cpp | 1 + tools/editor/cli_gen_mesh.cpp | 51 +++++++++++++++++++++++++++++++ tools/editor/cli_help.cpp | 2 ++ 3 files changed, 54 insertions(+) diff --git a/tools/editor/cli_arg_required.cpp b/tools/editor/cli_arg_required.cpp index ceadd12a..9d0cce31 100644 --- a/tools/editor/cli_arg_required.cpp +++ b/tools/editor/cli_arg_required.cpp @@ -120,6 +120,7 @@ const char* const kArgRequired[] = { "--gen-mesh-standing-torch", "--gen-mesh-scroll-case", "--gen-mesh-stove", "--gen-mesh-well-pail", "--gen-mesh-mug", "--gen-mesh-mortar-pestle", + "--gen-mesh-rune-stone", "--gen-camp-pack", "--gen-blacksmith-pack", "--gen-village-pack", "--gen-temple-pack", "--gen-graveyard-pack", "--gen-garden-pack", "--gen-dock-pack", "--gen-tavern-pack", diff --git a/tools/editor/cli_gen_mesh.cpp b/tools/editor/cli_gen_mesh.cpp index 4139aac0..d490020c 100644 --- a/tools/editor/cli_gen_mesh.cpp +++ b/tools/editor/cli_gen_mesh.cpp @@ -5261,6 +5261,56 @@ int handleMortarPestle(int& i, int argc, char** argv) { return 0; } +int handleRuneStone(int& i, int argc, char** argv) { + // Standing rune stone: a wide flat base block plus a tall + // narrow monolith standing on top. Reads as a small carved + // standing-stone marker — fits alongside --gen-mesh-altar + // / --gen-mesh-shrine / --gen-mesh-tombstone in the + // ritual-prop family. Distinct from --gen-mesh-tombstone + // (curved top, narrower) and --gen-mesh-pillar (round + // column). The 91st procedural mesh primitive. + std::string womBase = argv[++i]; + float baseW = 0.40f; // base block width (X) + float baseD = 0.30f; // base block depth (Z) + float baseH = 0.10f; // base block height (Y) + float stoneW = 0.20f; // monolith width + float stoneD = 0.12f; // monolith depth + float stoneH = 0.80f; // monolith height + parseOptFloat(i, argc, argv, baseW); + parseOptFloat(i, argc, argv, baseD); + parseOptFloat(i, argc, argv, baseH); + parseOptFloat(i, argc, argv, stoneW); + parseOptFloat(i, argc, argv, stoneD); + parseOptFloat(i, argc, argv, stoneH); + if (baseW <= 0 || baseD <= 0 || baseH <= 0 || + stoneW <= 0 || stoneD <= 0 || stoneH <= 0 || + stoneW > baseW || stoneD > baseD) { + std::fprintf(stderr, + "gen-mesh-rune-stone: dims > 0; stone(W,D) <= base(W,D)\n"); + return 1; + } + stripExt(womBase, ".wom"); + wowee::pipeline::WoweeModel wom; + initWomDefaults(wom, womBase); + // Base block sits on the ground. + addFlatBox(wom, 0.0f, baseH * 0.5f, 0.0f, + baseW * 0.5f, baseH * 0.5f, baseD * 0.5f); + // Monolith stands centered on the base. + const float stoneCY = baseH + stoneH * 0.5f; + addFlatBox(wom, 0.0f, stoneCY, 0.0f, + stoneW * 0.5f, stoneH * 0.5f, stoneD * 0.5f); + finalizeAsSingleBatch(wom); + setCenteredBoundsXZ(wom, baseW * 0.5f, baseD * 0.5f, baseH + stoneH); + if (!saveWomOrError(wom, womBase, "gen-mesh-rune-stone")) return 1; + printWomWrote(womBase); + std::printf(" base : %.2f x %.2f x %.2f tall\n", + baseW, baseD, baseH); + std::printf(" monolith : %.2f x %.2f x %.2f tall\n", + stoneW, stoneD, stoneH); + printWomMeshStats(wom); + return 0; +} + int handleWellPail(int& i, int argc, char** argv) { // Wooden well-pail / bucket: a closed cylindrical body // (collision-watertight) plus a thin horizontal handle bar @@ -7660,6 +7710,7 @@ constexpr MeshEntry kMeshTable[] = { {"--gen-mesh-well-pail", 1, handleWellPail}, {"--gen-mesh-mug", 1, handleMug}, {"--gen-mesh-mortar-pestle", 1, handleMortarPestle}, + {"--gen-mesh-rune-stone", 1, handleRuneStone}, {"--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 0b7a9ccc..0d4c12bc 100644 --- a/tools/editor/cli_help.cpp +++ b/tools/editor/cli_help.cpp @@ -346,6 +346,8 @@ void printUsage(const char* argv0) { std::printf(" Drinking mug / tankard: closed cylinder body + side handle slab (tavern / banquet / inn dressing)\n"); std::printf(" --gen-mesh-mortar-pestle [bowlR] [bowlH] [pestleR] [pestleH] [sides]\n"); std::printf(" Mortar + pestle: wide squat cylinder (bowl) + thin tall cylinder rising from inside (alchemy / kitchen)\n"); + std::printf(" --gen-mesh-rune-stone [baseW] [baseD] [baseH] [stoneW] [stoneD] [stoneH]\n"); + std::printf(" Rune stone: wide flat base block + tall narrow monolith (druid grove / witch shrine / ancient ruins)\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");