feat(editor): add --gen-camp-pack convenience composite

Convenience composite command: emits a complete outdoor-camp
scene (tent + firepit + bedroll + canopy + woodpile +
haystack) into <outDir>/ as 6 .wom files in one invocation.

Internally builds a synthetic argv array per primitive and
calls each existing handler — so the camp pack always
matches the per-primitive defaults exactly. Users wanting
custom dimensions should call the individual --gen-mesh-*
commands.

Smoke tested:
  • Creates outDir if needed
  • All 6 .wom files validate clean (--validate-wom PASSED)
  • Vertex / triangle counts match per-primitive defaults
    (firepit 240/120, woodpile 324/288, etc.)

The first composite command in the editor — establishes the
pattern for future packs (--gen-village-pack, --gen-dock-pack,
etc.) by showing how synthetic argv arrays let one handler
delegate to many others without spawning subprocesses.
This commit is contained in:
Kelsi 2026-05-09 12:22:37 -07:00
parent ba1f458ad3
commit 0b1cf65854
3 changed files with 62 additions and 0 deletions

View file

@ -55,6 +55,7 @@ const char* const kArgRequired[] = {
"--gen-mesh-workbench", "--gen-mesh-crate-stack",
"--gen-mesh-watchpost", "--gen-mesh-water-trough",
"--gen-mesh-training-dummy", "--gen-mesh-hitching-post",
"--gen-camp-pack",
"--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",

View file

@ -6314,6 +6314,64 @@ int handleFirepit(int& i, int argc, char** argv) {
return 0;
}
int handleGenCampPack(int& i, int /*argc*/, char** argv) {
// Convenience composite: emit a complete outdoor-camp scene
// (tent, firepit, bedroll, canopy, woodpile, haystack) into
// <outDir> in one command. Each primitive lands as its own
// .wom file using the existing handler — no per-primitive
// tweaking is exposed; users wanting custom dimensions should
// call the individual --gen-mesh-* commands directly.
std::string outDir = argv[++i];
std::error_code ec;
std::filesystem::create_directories(outDir, ec);
if (ec) {
std::fprintf(stderr,
"gen-camp-pack: cannot create %s: %s\n",
outDir.c_str(), ec.message().c_str());
return 1;
}
auto invoke = [&](int (*fn)(int&, int, char**),
const char* flag, const std::string& path) -> int {
// Build a synthetic argv where index 0 = the flag name and
// index 1 = the wom-base path (handlers do argv[++i] from
// the flag's position to read the base).
const char* args[2];
args[0] = flag;
args[1] = path.c_str();
std::vector<char*> mut;
mut.reserve(2);
for (auto* a : args) mut.push_back(const_cast<char*>(a));
int idx = 0;
return fn(idx, 2, mut.data());
};
struct Item {
const char* flag;
int (*fn)(int&, int, char**);
const char* leaf;
};
const Item items[] = {
{"--gen-mesh-tent", handleTent, "tent"},
{"--gen-mesh-firepit", handleFirepit, "firepit"},
{"--gen-mesh-bedroll", handleBedroll, "bedroll"},
{"--gen-mesh-canopy", handleCanopy, "canopy"},
{"--gen-mesh-woodpile", handleWoodpile, "woodpile"},
{"--gen-mesh-haystack", handleHaystack, "haystack"},
};
int produced = 0;
for (const auto& it : items) {
std::string path = outDir + "/" + it.leaf;
if (invoke(it.fn, it.flag, path) != 0) {
std::fprintf(stderr,
"gen-camp-pack: %s sub-handler failed\n", it.flag);
return 1;
}
++produced;
}
std::printf("\nWrote camp pack to %s/ — %d primitives\n",
outDir.c_str(), produced);
return 0;
}
} // namespace
namespace {
@ -6381,6 +6439,7 @@ constexpr MeshEntry kMeshTable[] = {
{"--gen-mesh-water-trough", 1, handleWaterTrough},
{"--gen-mesh-training-dummy", 1, handleTrainingDummy},
{"--gen-mesh-hitching-post", 1, handleHitchingPost},
{"--gen-camp-pack", 1, handleGenCampPack},
{"--gen-mesh-table", 1, handleTable},
{"--gen-mesh-lamppost", 1, handleLamppost},
{"--gen-mesh-bed", 1, handleBed},

View file

@ -260,6 +260,8 @@ void printUsage(const char* argv0) {
std::printf(" Training dummy: post + cubic torso + cross-bar arms + optional head (sparring / drill yard)\n");
std::printf(" --gen-mesh-hitching-post <wom-base> [span] [height] [postW] [barT] [capH]\n");
std::printf(" Hitching post: 2 vertical posts + horizontal cross-bar + optional decorative caps (stable / town square)\n");
std::printf(" --gen-camp-pack <outDir>\n");
std::printf(" Convenience: emit tent + firepit + bedroll + canopy + woodpile + haystack into outDir as 6 .wom files\n");
std::printf(" --gen-mesh-table <wom-base> [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 <wom-base> [poleH] [poleT] [baseSize] [lanternSize] [lanternH]\n");