mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-10 02:53:51 +00:00
feat(editor): add --gen-mesh-podium ceremony-platform primitive
56th procedural mesh: stepped pyramid speaker stand — configurable number of steps (2..8) tapering from base to top with equal step heights for visual rhythm, plus a small lectern box positioned at the back of the top platform so a speaker has room in front. Top platform is half the base footprint, with each step shrinking by an equal increment. Useful for throne rooms, ceremonial dais, NPC speaker positions, monument bases, judgment platforms. Defaults to 1.60m base × 3 steps with a 0.30m lectern (~0.96m total).
This commit is contained in:
parent
fca17592b1
commit
b1f6563c0a
3 changed files with 117 additions and 0 deletions
|
|
@ -4655,6 +4655,117 @@ int handleCoffin(int& i, int argc, char** argv) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int handlePodium(int& i, int argc, char** argv) {
|
||||
// Podium: 4-box stepped pyramid speaker stand — large
|
||||
// bottom step, medium middle step, small top platform,
|
||||
// and a small lectern box on top of the platform. Useful
|
||||
// for throne rooms, ceremonies, NPC speaker positions,
|
||||
// monument bases. The 56th procedural mesh primitive.
|
||||
std::string womBase = argv[++i];
|
||||
float baseSize = 1.60f; // bottom step width = depth
|
||||
float baseHeight = 0.20f;
|
||||
int stepCount = 3; // total stepped tiers (incl. top)
|
||||
float lecternSize = 0.30f; // lectern at the very top
|
||||
if (i + 1 < argc && argv[i + 1][0] != '-') {
|
||||
try { baseSize = std::stof(argv[++i]); } catch (...) {}
|
||||
}
|
||||
if (i + 1 < argc && argv[i + 1][0] != '-') {
|
||||
try { baseHeight = std::stof(argv[++i]); } catch (...) {}
|
||||
}
|
||||
if (i + 1 < argc && argv[i + 1][0] != '-') {
|
||||
try { stepCount = std::stoi(argv[++i]); } catch (...) {}
|
||||
}
|
||||
if (i + 1 < argc && argv[i + 1][0] != '-') {
|
||||
try { lecternSize = std::stof(argv[++i]); } catch (...) {}
|
||||
}
|
||||
if (baseSize <= 0 || baseHeight <= 0 || lecternSize <= 0 ||
|
||||
stepCount < 2 || stepCount > 8) {
|
||||
std::fprintf(stderr,
|
||||
"gen-mesh-podium: dims > 0; stepCount 2..8\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; };
|
||||
Face faces[6] = {
|
||||
{{0, 1, 0}, {1, 0, 0}, {0, 0, 1}},
|
||||
{{0,-1, 0}, {1, 0, 0}, {0, 0,-1}},
|
||||
{{1, 0, 0}, {0, 0, 1}, {0, 1, 0}},
|
||||
{{-1,0, 0}, {0, 0,-1}, {0, 1, 0}},
|
||||
{{0, 0, 1}, {-1,0, 0}, {0, 1, 0}},
|
||||
{{0, 0,-1}, {1, 0, 0}, {0, 1, 0}},
|
||||
};
|
||||
glm::vec3 c(cx, cy, cz);
|
||||
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 = glm::vec3(f.du.x*hx, f.du.y*hy, f.du.z*hz);
|
||||
glm::vec3 dv = glm::vec3(f.dv.x*hx, f.dv.y*hy, f.dv.z*hz);
|
||||
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});
|
||||
}
|
||||
};
|
||||
// Each step is shorter on each side by ~15% of base size,
|
||||
// and the top platform's footprint is half the base. Step
|
||||
// heights are equal for visual rhythm.
|
||||
float topSize = baseSize * 0.50f;
|
||||
float sizeStep = (baseSize - topSize) / (stepCount - 1);
|
||||
float stepHeight = baseHeight;
|
||||
for (int s = 0; s < stepCount; ++s) {
|
||||
float halfSide = (baseSize - s * sizeStep) * 0.5f;
|
||||
float stepCY = stepHeight * 0.5f + s * stepHeight;
|
||||
addBox(0, stepCY, 0, halfSide, stepHeight * 0.5f, halfSide);
|
||||
}
|
||||
// Lectern: small box on top of the top platform, at the
|
||||
// back so a speaker has room in front. Faces +Z.
|
||||
float halfL = lecternSize * 0.5f;
|
||||
float lecternH = lecternSize * 1.2f;
|
||||
float platformTopY = stepHeight * stepCount;
|
||||
float lecternCY = platformTopY + lecternH * 0.5f;
|
||||
float lecternZ = -topSize * 0.25f; // pushed back
|
||||
addBox(0, lecternCY, lecternZ,
|
||||
halfL, lecternH * 0.5f, halfL * 0.4f);
|
||||
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 totalH = lecternCY + lecternH * 0.5f;
|
||||
float halfBase = baseSize * 0.5f;
|
||||
wom.boundMin = glm::vec3(-halfBase, 0.0f, -halfBase);
|
||||
wom.boundMax = glm::vec3( halfBase, totalH, halfBase);
|
||||
if (!wowee::pipeline::WoweeModelLoader::save(wom, womBase)) {
|
||||
std::fprintf(stderr,
|
||||
"gen-mesh-podium: failed to save %s.wom\n", womBase.c_str());
|
||||
return 1;
|
||||
}
|
||||
std::printf("Wrote %s.wom\n", womBase.c_str());
|
||||
std::printf(" base : %.3f square × %.3f thick\n",
|
||||
baseSize, baseHeight);
|
||||
std::printf(" steps : %d (top %.3f square)\n", stepCount, topSize);
|
||||
std::printf(" lectern : %.3f wide (at back)\n", lecternSize);
|
||||
std::printf(" total H : %.3f\n", totalH);
|
||||
std::printf(" vertices : %zu\n", wom.vertices.size());
|
||||
std::printf(" triangles : %zu\n", wom.indices.size() / 3);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int handleSundial(int& i, int argc, char** argv) {
|
||||
// Sundial: 8-box garden timekeeper — square base plate at
|
||||
// floor, central vertical gnomon slab spanning the diameter
|
||||
|
|
@ -6862,6 +6973,9 @@ bool handleGenMesh(int& i, int argc, char** argv, int& outRc) {
|
|||
if (std::strcmp(argv[i], "--gen-mesh-sundial") == 0 && i + 1 < argc) {
|
||||
outRc = handleSundial(i, argc, argv); return true;
|
||||
}
|
||||
if (std::strcmp(argv[i], "--gen-mesh-podium") == 0 && i + 1 < argc) {
|
||||
outRc = handlePodium(i, argc, argv); return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -226,6 +226,8 @@ void printUsage(const char* argv0) {
|
|||
std::printf(" Scarecrow: cruciform body + cross arms + head + brimmed hat (default 1.80/1.40/0.06/0.22/0.32)\n");
|
||||
std::printf(" --gen-mesh-sundial <wom-base> [baseSize] [baseH] [gnomonH] [gnomonT]\n");
|
||||
std::printf(" Sundial: square base + central gnomon slab + 4 cardinal hour markers (default 0.80/0.06/0.35/0.04)\n");
|
||||
std::printf(" --gen-mesh-podium <wom-base> [baseSize] [stepH] [steps] [lecternSize]\n");
|
||||
std::printf(" Podium: stepped pyramid + lectern at back of top platform (default 1.60/0.20/3/0.30)\n");
|
||||
std::printf(" Procedural tree: cylindrical trunk + spherical foliage (default 0.1/2.0/0.7)\n");
|
||||
std::printf(" --displace-mesh <wom-base> <heightmap.png> [scale]\n");
|
||||
std::printf(" Offset each vertex along its normal by heightmap brightness × scale (default 1.0)\n");
|
||||
|
|
|
|||
|
|
@ -167,6 +167,7 @@ int main(int argc, char* argv[]) {
|
|||
"--gen-mesh-stool", "--gen-mesh-cauldron", "--gen-mesh-gate",
|
||||
"--gen-mesh-beehive", "--gen-mesh-weathervane",
|
||||
"--gen-mesh-scarecrow", "--gen-mesh-sundial",
|
||||
"--gen-mesh-podium",
|
||||
"--gen-texture-gradient",
|
||||
"--gen-mesh-from-heightmap", "--export-mesh-heightmap",
|
||||
"--displace-mesh",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue