From c0ea12d2cb88878c59d9d62774cf0812471a056d Mon Sep 17 00:00:00 2001 From: Kelsi Date: Sat, 9 May 2026 13:38:06 -0700 Subject: [PATCH] feat(editor): add --gen-mesh-lantern hand-lantern primitive MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 81st procedural mesh primitive. Hand-held oil lantern via 4 stacked Y-axis cylinders: • base — short narrow disc (the foot the lantern sits on) • globe — wider taller cylinder (the glass enclosure holding the wick) • neck — narrow constriction above the globe (where the cap meets the body) • cap — wider short top (the metal hood that lets vent smoke escape) Distinct from --gen-mesh-candle (just wax + saucer) and --gen-mesh-urn (4-tier pottery shape) — lantern's wide-narrow-wide tier sequence reads as a glass-enclosed wick under a metal hood. Useful for night-watch stations, ranger camps, dwarven mine tunnels, mausoleum interiors, vigil scenes, witch-hut shelves. Watertight under weld (verified 336 manifold edges, 0 boundary, 0 non-manifold). --- tools/editor/cli_arg_required.cpp | 1 + tools/editor/cli_gen_mesh.cpp | 59 +++++++++++++++++++++++++++++++ tools/editor/cli_help.cpp | 2 ++ 3 files changed, 62 insertions(+) diff --git a/tools/editor/cli_arg_required.cpp b/tools/editor/cli_arg_required.cpp index de042b61..fd6181e0 100644 --- a/tools/editor/cli_arg_required.cpp +++ b/tools/editor/cli_arg_required.cpp @@ -61,6 +61,7 @@ const char* const kArgRequired[] = { "--gen-mesh-hitching-rail", "--gen-mesh-pillar-row", "--gen-mesh-statue-base", "--gen-mesh-bird-bath", "--gen-mesh-planter-box", "--gen-mesh-urn", "--gen-mesh-candle", + "--gen-mesh-lantern", "--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 a993d3dc..c2a88251 100644 --- a/tools/editor/cli_gen_mesh.cpp +++ b/tools/editor/cli_gen_mesh.cpp @@ -5161,6 +5161,64 @@ int handleTent(int& i, int argc, char** argv) { return 0; } +int handleLantern(int& i, int argc, char** argv) { + // Hand lantern: small base disc → wider glass-globe cylinder + // → narrow neck → wider top cap. 4 cylindrical tiers stacked, + // mimicking a hooded oil-lantern silhouette. Distinct from + // --gen-mesh-candle (just wax + saucer) and --gen-mesh-urn + // (4-tier pottery shape) — lantern's wide-narrow-wide tier + // sequence reads as glass enclosed by metal hood. The 81st + // procedural mesh primitive. + std::string womBase = argv[++i]; + float baseR = 0.10f; + float baseH = 0.03f; + float globeR = 0.13f; + float globeH = 0.18f; + float neckR = 0.06f; + float neckH = 0.04f; + float capR = 0.14f; + float capH = 0.05f; + int sides = 14; + parseOptFloat(i, argc, argv, baseR); + parseOptFloat(i, argc, argv, baseH); + parseOptFloat(i, argc, argv, globeR); + parseOptFloat(i, argc, argv, globeH); + parseOptFloat(i, argc, argv, neckR); + parseOptFloat(i, argc, argv, neckH); + parseOptFloat(i, argc, argv, capR); + parseOptFloat(i, argc, argv, capH); + parseOptInt(i, argc, argv, sides); + if (baseR <= 0 || baseH <= 0 || globeR <= 0 || globeH <= 0 || + neckR <= 0 || neckH <= 0 || capR <= 0 || capH <= 0 || + sides < 6 || sides > 64) { + std::fprintf(stderr, + "gen-mesh-lantern: dims > 0; sides 6..64\n"); + return 1; + } + stripExt(womBase, ".wom"); + wowee::pipeline::WoweeModel wom; + initWomDefaults(wom, womBase); + float y = 0.0f; + addClosedCylinderY(wom, baseR, y, y + baseH, sides); + y += baseH; + addClosedCylinderY(wom, globeR, y, y + globeH, sides); + y += globeH; + addClosedCylinderY(wom, neckR, y, y + neckH, sides); + y += neckH; + addClosedCylinderY(wom, capR, y, y + capH, sides); + y += capH; + finalizeAsSingleBatch(wom); + float maxR = std::max({baseR, globeR, capR}); + setCenteredBoundsXZ(wom, maxR, maxR, y); + if (!saveWomOrError(wom, womBase, "gen-mesh-lantern")) return 1; + printWomWrote(womBase); + std::printf(" base / globe / neck / cap : %.3f / %.3f / %.3f / %.3f R\n", + baseR, globeR, neckR, capR); + std::printf(" total H : %.3f (%d sides)\n", y, sides); + printWomMeshStats(wom); + return 0; +} + int handleCandle(int& i, int argc, char** argv) { // Wax pillar candle: thin tall wax cylinder optionally // standing on a wider shallow saucer base (the drip catcher). @@ -7203,6 +7261,7 @@ constexpr MeshEntry kMeshTable[] = { {"--gen-mesh-planter-box", 1, handlePlanterBox}, {"--gen-mesh-urn", 1, handleUrn}, {"--gen-mesh-candle", 1, handleCandle}, + {"--gen-mesh-lantern", 1, handleLantern}, {"--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 0fef8f1f..30901813 100644 --- a/tools/editor/cli_help.cpp +++ b/tools/editor/cli_help.cpp @@ -312,6 +312,8 @@ void printUsage(const char* argv0) { std::printf(" Urn: 4-tier vertical pottery vessel (foot + body + neck + lip) — temple / mausoleum / kitchen storage\n"); std::printf(" --gen-mesh-candle [waxR] [waxH] [saucerR] [saucerH] [sides]\n"); std::printf(" Candle: thin wax pillar on optional saucer base (set saucerR=0 to skip) — chapels / vigil scenes\n"); + std::printf(" --gen-mesh-lantern [baseR] [baseH] [globeR] [globeH] [neckR] [neckH] [capR] [capH] [sides]\n"); + std::printf(" Lantern: 4-tier base + glass-globe + neck + cap stack (hand lantern / oil lamp silhouette)\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");