mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-10 02:53:51 +00:00
feat(editor): add --gen-mesh-firepit camp firepit primitive
54th procedural mesh primitive. Builds a recognizable
campfire setup from axis-aligned boxes only:
• ring of N stone cubes evenly placed at radius ringR
around the firepit center, sitting on the ground
• two crossed log boxes at the center (one along X,
one along Z, slightly raised) — the unmistakable
visual cue separating a firepit from a generic
decorative stone ring
Pairs naturally with --gen-mesh-tent for outdoor camp
set dressing (Horde encampments, Defias hideouts, scout
overlooks). Default 0.5-radius ring with 8 stones reads
cleanly at 1:1 player scale.
This commit is contained in:
parent
86377df7ad
commit
17a53f192f
3 changed files with 115 additions and 1 deletions
|
|
@ -49,7 +49,7 @@ const char* const kArgRequired[] = {
|
|||
"--gen-mesh-banner", "--gen-mesh-grave", "--gen-mesh-bench",
|
||||
"--gen-mesh-shrine", "--gen-mesh-totem", "--gen-mesh-cage",
|
||||
"--gen-mesh-throne", "--gen-mesh-coffin", "--gen-mesh-bookshelf",
|
||||
"--gen-mesh-tent",
|
||||
"--gen-mesh-tent", "--gen-mesh-firepit",
|
||||
"--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",
|
||||
|
|
|
|||
|
|
@ -7192,6 +7192,117 @@ int handleTent(int& i, int argc, char** argv) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int handleFirepit(int& i, int argc, char** argv) {
|
||||
// Camp firepit: a ring of N stone cubes around two crossed log
|
||||
// boxes (one along X, one along Z, slightly raised). Pairs
|
||||
// naturally with --gen-mesh-tent for outdoor camp set dressing.
|
||||
// The 54th procedural mesh primitive.
|
||||
std::string womBase = argv[++i];
|
||||
float ringR = 0.5f;
|
||||
int stones = 8;
|
||||
float stoneSize = 0.10f;
|
||||
float logLen = 0.45f;
|
||||
float logThick = 0.05f;
|
||||
if (i + 1 < argc && argv[i + 1][0] != '-') {
|
||||
try { ringR = std::stof(argv[++i]); } catch (...) {}
|
||||
}
|
||||
if (i + 1 < argc && argv[i + 1][0] != '-') {
|
||||
try { stones = std::stoi(argv[++i]); } catch (...) {}
|
||||
}
|
||||
if (i + 1 < argc && argv[i + 1][0] != '-') {
|
||||
try { stoneSize = std::stof(argv[++i]); } catch (...) {}
|
||||
}
|
||||
if (i + 1 < argc && argv[i + 1][0] != '-') {
|
||||
try { logLen = std::stof(argv[++i]); } catch (...) {}
|
||||
}
|
||||
if (i + 1 < argc && argv[i + 1][0] != '-') {
|
||||
try { logThick = std::stof(argv[++i]); } catch (...) {}
|
||||
}
|
||||
if (ringR <= 0 || stoneSize <= 0 || logLen <= 0 || logThick <= 0 ||
|
||||
stones < 3 || stones > 64) {
|
||||
std::fprintf(stderr,
|
||||
"gen-mesh-firepit: dims > 0; stones must be 3..64\n");
|
||||
return 1;
|
||||
}
|
||||
if (womBase.size() >= 4 &&
|
||||
womBase.substr(womBase.size() - 4) == ".wom") {
|
||||
womBase = womBase.substr(0, womBase.size() - 4);
|
||||
}
|
||||
wowee::pipeline::WoweeModel wom;
|
||||
wom.name = std::filesystem::path(womBase).stem().string();
|
||||
wom.version = 3;
|
||||
auto addBox = [&](float cx, float cy, float cz,
|
||||
float hx, float hy, float hz) {
|
||||
struct Face { glm::vec3 n, du, dv; };
|
||||
const Face faces[6] = {
|
||||
{{0, 1, 0}, {1, 0, 0}, {0, 0, 1}}, // +Y
|
||||
{{0,-1, 0}, {1, 0, 0}, {0, 0,-1}}, // -Y
|
||||
{{1, 0, 0}, {0, 0, 1}, {0, 1, 0}}, // +X
|
||||
{{-1,0, 0}, {0, 0,-1}, {0, 1, 0}}, // -X
|
||||
{{0, 0, 1}, {-1,0, 0}, {0, 1, 0}}, // +Z
|
||||
{{0, 0,-1}, {1, 0, 0}, {0, 1, 0}}, // -Z
|
||||
};
|
||||
glm::vec3 c(cx, cy, cz);
|
||||
glm::vec3 ext(hx, hy, hz);
|
||||
for (const Face& f : faces) {
|
||||
glm::vec3 center = c + glm::vec3(f.n.x*hx, f.n.y*hy, f.n.z*hz);
|
||||
glm::vec3 du(f.du.x*ext.x, f.du.y*ext.y, f.du.z*ext.z);
|
||||
glm::vec3 dv(f.dv.x*ext.x, f.dv.y*ext.y, f.dv.z*ext.z);
|
||||
uint32_t base = static_cast<uint32_t>(wom.vertices.size());
|
||||
auto push = [&](glm::vec3 p, float u, float v) {
|
||||
wowee::pipeline::WoweeModel::Vertex vtx;
|
||||
vtx.position = p; vtx.normal = f.n; vtx.texCoord = {u, v};
|
||||
wom.vertices.push_back(vtx);
|
||||
};
|
||||
push(center - du - dv, 0, 0);
|
||||
push(center + du - dv, 1, 0);
|
||||
push(center + du + dv, 1, 1);
|
||||
push(center - du + dv, 0, 1);
|
||||
wom.indices.insert(wom.indices.end(),
|
||||
{base, base+1, base+2, base, base+2, base+3});
|
||||
}
|
||||
};
|
||||
// Ring of stones — N axis-aligned cube stones evenly placed
|
||||
// around the firepit center. Slight Y offset puts them sitting
|
||||
// on the ground rather than sunk into it.
|
||||
const float pi = 3.14159265358979f;
|
||||
for (int s = 0; s < stones; ++s) {
|
||||
float ang = (2.0f * pi * s) / stones;
|
||||
float cx = ringR * std::cos(ang);
|
||||
float cz = ringR * std::sin(ang);
|
||||
addBox(cx, stoneSize, cz, stoneSize, stoneSize, stoneSize);
|
||||
}
|
||||
// Two crossed logs at center, raised so they sit on the ash
|
||||
// bed. The two-log cross is the unmistakable visual cue that
|
||||
// separates a firepit from a generic stone ring.
|
||||
float logCY = logThick;
|
||||
addBox(0, logCY, 0, logLen * 0.5f, logThick * 0.5f, logThick * 0.5f);
|
||||
addBox(0, logCY + logThick, 0, logThick * 0.5f, logThick * 0.5f,
|
||||
logLen * 0.5f);
|
||||
wowee::pipeline::WoweeModel::Batch batch;
|
||||
batch.indexStart = 0;
|
||||
batch.indexCount = static_cast<uint32_t>(wom.indices.size());
|
||||
batch.textureIndex = 0;
|
||||
wom.batches.push_back(batch);
|
||||
float maxR = std::max(ringR + stoneSize, logLen * 0.5f);
|
||||
float maxY = std::max(stoneSize * 2.0f, logCY + logThick * 1.5f);
|
||||
wom.boundMin = glm::vec3(-maxR, 0, -maxR);
|
||||
wom.boundMax = glm::vec3( maxR, maxY, maxR);
|
||||
if (!wowee::pipeline::WoweeModelLoader::save(wom, womBase)) {
|
||||
std::fprintf(stderr,
|
||||
"gen-mesh-firepit: failed to save %s.wom\n", womBase.c_str());
|
||||
return 1;
|
||||
}
|
||||
std::printf("Wrote %s.wom\n", womBase.c_str());
|
||||
std::printf(" ring : R=%.3f, %d stones (%.3f cubes)\n",
|
||||
ringR, stones, stoneSize);
|
||||
std::printf(" logs : 2 crossed (len %.3f, thick %.3f)\n",
|
||||
logLen, logThick);
|
||||
std::printf(" vertices : %zu\n", wom.vertices.size());
|
||||
std::printf(" triangles : %zu\n", wom.indices.size() / 3);
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace {
|
||||
|
|
@ -7245,6 +7356,7 @@ constexpr MeshEntry kMeshTable[] = {
|
|||
{"--gen-mesh-coffin", 1, handleCoffin},
|
||||
{"--gen-mesh-bookshelf", 1, handleBookshelf},
|
||||
{"--gen-mesh-tent", 1, handleTent},
|
||||
{"--gen-mesh-firepit", 1, handleFirepit},
|
||||
{"--gen-mesh-table", 1, handleTable},
|
||||
{"--gen-mesh-lamppost", 1, handleLamppost},
|
||||
{"--gen-mesh-bed", 1, handleBed},
|
||||
|
|
|
|||
|
|
@ -204,6 +204,8 @@ void printUsage(const char* argv0) {
|
|||
std::printf(" Bookshelf: 5-panel cabinet with N-1 shelves and rows of varied book boxes (default 1.5/2.0/0.4/4)\n");
|
||||
std::printf(" --gen-mesh-tent <wom-base> [length] [width] [height] [doorH] [doorW]\n");
|
||||
std::printf(" Tent: A-frame canvas tent — ridge along X, two sloped roof panels, two gables, door notch on +X (default 1.6/1.0/0.9/0.5/0.4)\n");
|
||||
std::printf(" --gen-mesh-firepit <wom-base> [ringR] [stones] [stoneSize] [logLen] [logThick]\n");
|
||||
std::printf(" Firepit: ring of N stone cubes around 2 crossed log boxes (default 0.5/8/0.10/0.45/0.05)\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");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue