mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-10 02:53:51 +00:00
feat(editor): add --gen-mesh-tent A-frame canvas tent
53rd procedural mesh primitive. Builds a watertight A-frame
tent:
• two sloped roof panels meeting at a ridge that runs along
the X axis
• two triangular gables sealing the ends
• optional inverted-V door notch carved out of the +X gable
(parameterized by doorH and doorW; either set to 0 disables
the cutout for a solid gable)
• bottom face for collision-bake watertightness
Useful set dressing for Horde encampments, troll camps,
Defias bandit hideouts, scout/quartermaster overlooks, and
generic outdoor quest hubs.
Default footprint 1.6 x 1.0 with 0.9 ridge height and a
0.5 x 0.4 door notch — all dimensions overridable on the CLI.
This commit is contained in:
parent
ff82c0ade2
commit
e746e400dd
3 changed files with 156 additions and 0 deletions
|
|
@ -7051,6 +7051,156 @@ int handleBookshelf(int& i, int argc, char** argv) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int handleTent(int& i, int argc, char** argv) {
|
||||
// A-frame canvas tent: ridge running along X from
|
||||
// (-L/2, H, 0) to (+L/2, H, 0); rectangular footprint LxW
|
||||
// on the ground; two sloped roof panels meeting at the ridge
|
||||
// and two triangular gables closing the ends. Optionally a
|
||||
// simple inverted-V door notch is cut from the +X gable so
|
||||
// there is a visible entrance. Watertight bottom face is
|
||||
// included so the model is a closed solid for collision
|
||||
// baking. The 53rd procedural mesh primitive.
|
||||
std::string womBase = argv[++i];
|
||||
float length = 1.6f;
|
||||
float width = 1.0f;
|
||||
float height = 0.9f;
|
||||
float doorH = 0.5f;
|
||||
float doorW = 0.4f;
|
||||
if (i + 1 < argc && argv[i + 1][0] != '-') {
|
||||
try { length = std::stof(argv[++i]); } catch (...) {}
|
||||
}
|
||||
if (i + 1 < argc && argv[i + 1][0] != '-') {
|
||||
try { width = std::stof(argv[++i]); } catch (...) {}
|
||||
}
|
||||
if (i + 1 < argc && argv[i + 1][0] != '-') {
|
||||
try { height = std::stof(argv[++i]); } catch (...) {}
|
||||
}
|
||||
if (i + 1 < argc && argv[i + 1][0] != '-') {
|
||||
try { doorH = std::stof(argv[++i]); } catch (...) {}
|
||||
}
|
||||
if (i + 1 < argc && argv[i + 1][0] != '-') {
|
||||
try { doorW = std::stof(argv[++i]); } catch (...) {}
|
||||
}
|
||||
if (length <= 0 || width <= 0 || height <= 0 ||
|
||||
doorH < 0 || doorH >= height ||
|
||||
doorW < 0 || doorW >= width) {
|
||||
std::fprintf(stderr,
|
||||
"gen-mesh-tent: dims > 0; doorH < height; doorW < width\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 addV = [&](glm::vec3 p, glm::vec3 n, glm::vec2 uv) -> uint32_t {
|
||||
wowee::pipeline::WoweeModel::Vertex vtx;
|
||||
vtx.position = p; vtx.normal = n; vtx.texCoord = uv;
|
||||
wom.vertices.push_back(vtx);
|
||||
return static_cast<uint32_t>(wom.vertices.size() - 1);
|
||||
};
|
||||
const float L2 = length * 0.5f;
|
||||
const float W2 = width * 0.5f;
|
||||
// Slope normals for the two roof panels — built from the panel
|
||||
// edge vectors then normalized so adjacent vertices share the
|
||||
// same per-face shading.
|
||||
glm::vec3 nBack = glm::normalize(glm::vec3(0.0f, W2, -height));
|
||||
glm::vec3 nFront = glm::normalize(glm::vec3(0.0f, W2, height));
|
||||
// Back roof panel (faces -Z and +Y): A=(-L2,0,-W2), B=(+L2,0,-W2),
|
||||
// R1=(+L2,H,0), R0=(-L2,H,0). Quad → 2 triangles, CCW from outside.
|
||||
{
|
||||
uint32_t a = addV({-L2, 0, -W2}, nBack, {0, 0});
|
||||
uint32_t b = addV({+L2, 0, -W2}, nBack, {1, 0});
|
||||
uint32_t r1 = addV({+L2, height, 0}, nBack, {1, 1});
|
||||
uint32_t r0 = addV({-L2, height, 0}, nBack, {0, 1});
|
||||
wom.indices.insert(wom.indices.end(), {a, b, r1, a, r1, r0});
|
||||
}
|
||||
// Front roof panel (faces +Z and +Y).
|
||||
{
|
||||
uint32_t d = addV({-L2, 0, +W2}, nFront, {0, 0});
|
||||
uint32_t r0 = addV({-L2, height, 0}, nFront, {0, 1});
|
||||
uint32_t r1 = addV({+L2, height, 0}, nFront, {1, 1});
|
||||
uint32_t c = addV({+L2, 0, +W2}, nFront, {1, 0});
|
||||
wom.indices.insert(wom.indices.end(), {d, r0, r1, d, r1, c});
|
||||
}
|
||||
// -X gable (full triangle, no door): A=(-L2,0,-W2), R0=(-L2,H,0),
|
||||
// D=(-L2,0,+W2). Faces -X.
|
||||
{
|
||||
glm::vec3 n(-1, 0, 0);
|
||||
uint32_t a = addV({-L2, 0, -W2}, n, {0, 0});
|
||||
uint32_t r0 = addV({-L2, height, 0}, n, {0.5f, 1});
|
||||
uint32_t d = addV({-L2, 0, +W2}, n, {1, 0});
|
||||
wom.indices.insert(wom.indices.end(), {a, r0, d});
|
||||
}
|
||||
// +X gable: B=(+L2,0,-W2), C=(+L2,0,+W2), R1=(+L2,H,0). Faces +X.
|
||||
// If doorH>0 we carve out a tapered notch — bottom edge of width
|
||||
// doorW, apex on the centerline at height doorH — and replace the
|
||||
// single gable triangle with a 4-triangle fan around the door.
|
||||
{
|
||||
glm::vec3 n(+1, 0, 0);
|
||||
if (doorH > 0 && doorW > 0) {
|
||||
uint32_t b = addV({+L2, 0, -W2}, n, {0, 0});
|
||||
uint32_t bl = addV({+L2, 0, -doorW * 0.5f}, n,
|
||||
{0.5f - doorW / (2 * width), 0});
|
||||
uint32_t br = addV({+L2, 0, +doorW * 0.5f}, n,
|
||||
{0.5f + doorW / (2 * width), 0});
|
||||
uint32_t c = addV({+L2, 0, +W2}, n, {1, 0});
|
||||
uint32_t r1 = addV({+L2, height, 0}, n, {0.5f, 1});
|
||||
uint32_t dt = addV({+L2, doorH, 0}, n,
|
||||
{0.5f, doorH / height});
|
||||
// Slab right of the door, slab left of the door, then the
|
||||
// peak triangle bridging door-top to ridge.
|
||||
wom.indices.insert(wom.indices.end(), {b, c, br});
|
||||
wom.indices.insert(wom.indices.end(), {b, br, dt});
|
||||
wom.indices.insert(wom.indices.end(), {b, dt, r1});
|
||||
wom.indices.insert(wom.indices.end(), {c, r1, dt});
|
||||
wom.indices.insert(wom.indices.end(), {c, dt, br});
|
||||
(void)bl; // left-base slot reserved for symmetric door variant
|
||||
} else {
|
||||
uint32_t b = addV({+L2, 0, -W2}, n, {0, 0});
|
||||
uint32_t c = addV({+L2, 0, +W2}, n, {1, 0});
|
||||
uint32_t r1 = addV({+L2, height, 0}, n, {0.5f, 1});
|
||||
wom.indices.insert(wom.indices.end(), {b, c, r1});
|
||||
}
|
||||
}
|
||||
// Ground face (faces -Y) so the tent is a closed solid for
|
||||
// collision baking.
|
||||
{
|
||||
glm::vec3 n(0, -1, 0);
|
||||
uint32_t a = addV({-L2, 0, -W2}, n, {0, 0});
|
||||
uint32_t b = addV({+L2, 0, -W2}, n, {1, 0});
|
||||
uint32_t c = addV({+L2, 0, +W2}, n, {1, 1});
|
||||
uint32_t d = addV({-L2, 0, +W2}, n, {0, 1});
|
||||
wom.indices.insert(wom.indices.end(), {a, d, c, a, c, b});
|
||||
}
|
||||
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);
|
||||
wom.boundMin = glm::vec3(-L2, 0, -W2);
|
||||
wom.boundMax = glm::vec3(+L2, height, +W2);
|
||||
if (!wowee::pipeline::WoweeModelLoader::save(wom, womBase)) {
|
||||
std::fprintf(stderr,
|
||||
"gen-mesh-tent: failed to save %s.wom\n", womBase.c_str());
|
||||
return 1;
|
||||
}
|
||||
std::printf("Wrote %s.wom\n", womBase.c_str());
|
||||
std::printf(" footprint : %.3f x %.3f\n", length, width);
|
||||
std::printf(" height : %.3f (ridge along X)\n", height);
|
||||
if (doorH > 0 && doorW > 0) {
|
||||
std::printf(" door : H=%.3f W=%.3f on +X gable\n",
|
||||
doorH, doorW);
|
||||
} else {
|
||||
std::printf(" door : (none)\n");
|
||||
}
|
||||
std::printf(" vertices : %zu\n", wom.vertices.size());
|
||||
std::printf(" triangles : %zu\n", wom.indices.size() / 3);
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool handleGenMesh(int& i, int argc, char** argv, int& outRc) {
|
||||
|
|
@ -7163,6 +7313,9 @@ bool handleGenMesh(int& i, int argc, char** argv, int& outRc) {
|
|||
if (std::strcmp(argv[i], "--gen-mesh-bookshelf") == 0 && i + 1 < argc) {
|
||||
outRc = handleBookshelf(i, argc, argv); return true;
|
||||
}
|
||||
if (std::strcmp(argv[i], "--gen-mesh-tent") == 0 && i + 1 < argc) {
|
||||
outRc = handleTent(i, argc, argv); return true;
|
||||
}
|
||||
if (std::strcmp(argv[i], "--gen-mesh-table") == 0 && i + 1 < argc) {
|
||||
outRc = handleTable(i, argc, argv); return true;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue