mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-10 02:53:51 +00:00
feat(editor): add --gen-mesh-archery-target training prop
70th procedural mesh primitive. Round-faced archery target on
a 2-post stand:
• face: closed cylinder along the Z axis (flat ±Z caps face
the archer line, side wall is the rim) of radius faceR
centered at height postH
• stand: 2 vertical posts at the bottom, sized to reach
from ground to the bottom of the face, with a horizontal
cross-beam joining them just below the face for rigidity
Concentric scoring rings live in texture space, not geometry —
the cap discs are the natural canvas for a separate target-
ring texture (or use --gen-texture-rings / --gen-texture-
starburst on top).
Pairs naturally with --gen-mesh-training-dummy /
--gen-mesh-fence for sparring grounds, training yards,
militia drill squares, mid-summer fair scenes.
Watertight under weld (verified 198 manifold edges, 0
boundary, 0 non-manifold).
This commit is contained in:
parent
78c7aed888
commit
26af6d9df6
3 changed files with 125 additions and 0 deletions
|
|
@ -5193,6 +5193,127 @@ int handleTent(int& i, int argc, char** argv) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int handleArcheryTarget(int& i, int argc, char** argv) {
|
||||
// Archery target: round face on a 2-post stand. The face is a
|
||||
// short cylinder oriented along the Z axis (its flat circular
|
||||
// surfaces face ±Z, where archers shoot from). Stand uses two
|
||||
// vertical posts joined by a horizontal cross-beam underneath
|
||||
// the face. The 70th procedural mesh primitive.
|
||||
std::string womBase = argv[++i];
|
||||
float faceR = 0.40f; // target face radius
|
||||
float faceT = 0.08f; // target face thickness (Z extent)
|
||||
int sides = 24; // face cylinder smoothness
|
||||
float postH = 1.2f; // height from ground to face center
|
||||
float postW = 0.08f; // post thickness
|
||||
float beamT = 0.06f; // cross-beam thickness
|
||||
parseOptFloat(i, argc, argv, faceR);
|
||||
parseOptFloat(i, argc, argv, faceT);
|
||||
parseOptInt(i, argc, argv, sides);
|
||||
parseOptFloat(i, argc, argv, postH);
|
||||
parseOptFloat(i, argc, argv, postW);
|
||||
parseOptFloat(i, argc, argv, beamT);
|
||||
if (faceR <= 0 || faceT <= 0 || sides < 6 || sides > 64 ||
|
||||
postH <= 0 || postW <= 0 || beamT <= 0 ||
|
||||
postW * 2 >= faceR * 2) {
|
||||
std::fprintf(stderr,
|
||||
"gen-mesh-archery-target: dims > 0; sides 6..64; "
|
||||
"postW*2 < faceR*2\n");
|
||||
return 1;
|
||||
}
|
||||
stripExt(womBase, ".wom");
|
||||
wowee::pipeline::WoweeModel wom;
|
||||
initWomDefaults(wom, womBase);
|
||||
const float pi = 3.14159265358979f;
|
||||
const float halfStanceX = faceR + postW * 0.5f;
|
||||
// Two vertical posts of the stand, sized to reach from ground
|
||||
// to the bottom of the face.
|
||||
const float postTopY = postH - faceR;
|
||||
if (postTopY > 0.0f) {
|
||||
addFlatBox(wom, +halfStanceX, postTopY * 0.5f, 0.0f,
|
||||
postW * 0.5f, postTopY * 0.5f, postW * 0.5f);
|
||||
addFlatBox(wom, -halfStanceX, postTopY * 0.5f, 0.0f,
|
||||
postW * 0.5f, postTopY * 0.5f, postW * 0.5f);
|
||||
}
|
||||
// Horizontal cross-beam underneath the face joining the two
|
||||
// posts (gives the stand visual rigidity).
|
||||
const float beamCY = postTopY - beamT * 0.5f;
|
||||
if (beamCY > 0.0f) {
|
||||
addFlatBox(wom, 0.0f, beamCY, 0.0f,
|
||||
halfStanceX, beamT * 0.5f, beamT * 0.5f);
|
||||
}
|
||||
// Target face: closed cylinder along the Z axis centered at
|
||||
// (0, postH, 0). Flat ±Z caps face the archer line; side
|
||||
// wall is the rim.
|
||||
const float halfT = faceT * 0.5f;
|
||||
uint32_t back = static_cast<uint32_t>(wom.vertices.size());
|
||||
for (int s = 0; s <= sides; ++s) {
|
||||
float u = static_cast<float>(s) / sides;
|
||||
float ang = u * 2.0f * pi;
|
||||
glm::vec3 dir(std::cos(ang), std::sin(ang), 0.0f);
|
||||
glm::vec3 p(faceR * dir.x, postH + faceR * dir.y, -halfT);
|
||||
addVertex(wom, p, dir, {u, 0});
|
||||
}
|
||||
uint32_t front = static_cast<uint32_t>(wom.vertices.size());
|
||||
for (int s = 0; s <= sides; ++s) {
|
||||
float u = static_cast<float>(s) / sides;
|
||||
float ang = u * 2.0f * pi;
|
||||
glm::vec3 dir(std::cos(ang), std::sin(ang), 0.0f);
|
||||
glm::vec3 p(faceR * dir.x, postH + faceR * dir.y, +halfT);
|
||||
addVertex(wom, p, dir, {u, 1});
|
||||
}
|
||||
for (int s = 0; s < sides; ++s) {
|
||||
wom.indices.insert(wom.indices.end(), {
|
||||
back + s, front + s, back + s + 1,
|
||||
back + s + 1, front + s, front + s + 1
|
||||
});
|
||||
}
|
||||
// Cap fans on -Z and +Z so the face is closed.
|
||||
uint32_t backCenter = addVertex(wom, {0, postH, -halfT},
|
||||
{0, 0, -1}, {0.5f, 0.5f});
|
||||
uint32_t backRing = static_cast<uint32_t>(wom.vertices.size());
|
||||
for (int s = 0; s <= sides; ++s) {
|
||||
float u = static_cast<float>(s) / sides;
|
||||
float ang = u * 2.0f * pi;
|
||||
glm::vec3 p(faceR * std::cos(ang),
|
||||
postH + faceR * std::sin(ang), -halfT);
|
||||
addVertex(wom, p, {0, 0, -1},
|
||||
{0.5f + 0.5f * std::cos(ang),
|
||||
0.5f + 0.5f * std::sin(ang)});
|
||||
}
|
||||
for (int s = 0; s < sides; ++s) {
|
||||
wom.indices.insert(wom.indices.end(),
|
||||
{backCenter, backRing + s + 1, backRing + s});
|
||||
}
|
||||
uint32_t frontCenter = addVertex(wom, {0, postH, +halfT},
|
||||
{0, 0, +1}, {0.5f, 0.5f});
|
||||
uint32_t frontRing = static_cast<uint32_t>(wom.vertices.size());
|
||||
for (int s = 0; s <= sides; ++s) {
|
||||
float u = static_cast<float>(s) / sides;
|
||||
float ang = u * 2.0f * pi;
|
||||
glm::vec3 p(faceR * std::cos(ang),
|
||||
postH + faceR * std::sin(ang), +halfT);
|
||||
addVertex(wom, p, {0, 0, +1},
|
||||
{0.5f + 0.5f * std::cos(ang),
|
||||
0.5f + 0.5f * std::sin(ang)});
|
||||
}
|
||||
for (int s = 0; s < sides; ++s) {
|
||||
wom.indices.insert(wom.indices.end(),
|
||||
{frontCenter, frontRing + s, frontRing + s + 1});
|
||||
}
|
||||
finalizeAsSingleBatch(wom);
|
||||
setCenteredBoundsXZ(wom, halfStanceX + postW * 0.5f, halfT,
|
||||
postH + faceR);
|
||||
if (!saveWomOrError(wom, womBase, "gen-mesh-archery-target")) return 1;
|
||||
std::printf("Wrote %s.wom\n", womBase.c_str());
|
||||
std::printf(" face : R=%.3f x %.3f deep, %d sides\n",
|
||||
faceR, faceT, sides);
|
||||
std::printf(" stand : posts at ±%.3f, beam %.3f thick\n",
|
||||
halfStanceX, beamT);
|
||||
std::printf(" vertices : %zu\n", wom.vertices.size());
|
||||
std::printf(" triangles : %zu\n", wom.indices.size() / 3);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int handleForge(int& i, int argc, char** argv) {
|
||||
// Blacksmith forge: rectangular stone hearth with a smaller
|
||||
// hood on top and an optional thin chimney rising from the
|
||||
|
|
@ -6593,6 +6714,7 @@ constexpr MeshEntry kMeshTable[] = {
|
|||
{"--gen-mesh-hitching-post", 1, handleHitchingPost},
|
||||
{"--gen-mesh-outhouse", 1, handleOuthouse},
|
||||
{"--gen-mesh-forge", 1, handleForge},
|
||||
{"--gen-mesh-archery-target", 1, handleArcheryTarget},
|
||||
{"--gen-camp-pack", 1, handleGenCampPack},
|
||||
{"--gen-blacksmith-pack", 1, handleGenBlacksmithPack},
|
||||
{"--gen-mesh-table", 1, handleTable},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue