mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-10 02:53:51 +00:00
feat(editor): add --gen-texture-runes magical-glyph pattern
44th procedural texture: scattered angular runes drawn as 3-5 random stroke segments per glyph. Each stroke uses one of 8 cardinal/diagonal angles (0/45/90/135/...°) so the strokes read as deliberate runic carvings rather than random scribbles. Layout is a sparse grid with per-slot jitter and ~5% empty slots so the result looks hand-carved rather than mechanical. Useful for ancient ruins, magical zones, dwarven walls, necromancer altars, druidic shrines. Defaults to a 64-px grid spacing yielding ~15 runes in a 256×256 image.
This commit is contained in:
parent
710fdf9b35
commit
288c4e93b6
3 changed files with 124 additions and 1 deletions
|
|
@ -4182,6 +4182,124 @@ int handleCracked(int& i, int argc, char** argv) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
int handleRunes(int& i, int argc, char** argv) {
|
||||
// Runes: magical glyphs scattered on a textured background.
|
||||
// Each rune is 3-5 random line segments emanating from a
|
||||
// center point; segments use 8 cardinal/diagonal angles
|
||||
// (0°, 45°, 90°, ...) so the strokes read as deliberate
|
||||
// angular runes rather than random scribbles. Layout is a
|
||||
// sparse grid with per-rune jitter so they look hand-carved.
|
||||
std::string outPath = argv[++i];
|
||||
std::string bgHex = argv[++i];
|
||||
std::string runeHex = argv[++i];
|
||||
int gridSpacing = 64; // rune slot size
|
||||
int W = 256, H = 256;
|
||||
if (i + 1 < argc && argv[i + 1][0] != '-') {
|
||||
try { gridSpacing = std::stoi(argv[++i]); } catch (...) {}
|
||||
}
|
||||
if (i + 1 < argc && argv[i + 1][0] != '-') {
|
||||
try { W = std::stoi(argv[++i]); } catch (...) {}
|
||||
}
|
||||
if (i + 1 < argc && argv[i + 1][0] != '-') {
|
||||
try { H = std::stoi(argv[++i]); } catch (...) {}
|
||||
}
|
||||
if (W < 1 || H < 1 || W > 8192 || H > 8192 ||
|
||||
gridSpacing < 16 || gridSpacing > 512) {
|
||||
std::fprintf(stderr,
|
||||
"gen-texture-runes: invalid dims (W/H 1..8192, gridSpacing 16..512)\n");
|
||||
return 1;
|
||||
}
|
||||
uint8_t br_, bg_, bb_, rr_, rg_, rb_;
|
||||
if (!parseHex(bgHex, br_, bg_, bb_) ||
|
||||
!parseHex(runeHex, rr_, rg_, rb_)) {
|
||||
std::fprintf(stderr,
|
||||
"gen-texture-runes: bg or rune hex color is invalid\n");
|
||||
return 1;
|
||||
}
|
||||
std::vector<uint8_t> pixels(static_cast<size_t>(W) * H * 3, 0);
|
||||
for (size_t p = 0; p < pixels.size(); p += 3) {
|
||||
pixels[p + 0] = br_;
|
||||
pixels[p + 1] = bg_;
|
||||
pixels[p + 2] = bb_;
|
||||
}
|
||||
auto paint = [&](int x, int y) {
|
||||
if (x < 0 || x >= W || y < 0 || y >= H) return;
|
||||
size_t idx = (static_cast<size_t>(y) * W + x) * 3;
|
||||
pixels[idx + 0] = rr_;
|
||||
pixels[idx + 1] = rg_;
|
||||
pixels[idx + 2] = rb_;
|
||||
};
|
||||
auto drawLine = [&](int x0, int y0, int x1, int y1) {
|
||||
// Bresenham. Pixels paint with the rune color.
|
||||
int dx = std::abs(x1 - x0), sx = x0 < x1 ? 1 : -1;
|
||||
int dy = -std::abs(y1 - y0), sy = y0 < y1 ? 1 : -1;
|
||||
int err = dx + dy;
|
||||
while (true) {
|
||||
paint(x0, y0);
|
||||
if (x0 == x1 && y0 == y1) break;
|
||||
int e2 = 2 * err;
|
||||
if (e2 >= dy) { err += dy; x0 += sx; }
|
||||
if (e2 <= dx) { err += dx; y0 += sy; }
|
||||
}
|
||||
};
|
||||
// 8 cardinal/diagonal direction unit vectors.
|
||||
static const int kDir[8][2] = {
|
||||
{ 1, 0}, { 1, 1}, {0, 1}, {-1, 1},
|
||||
{-1, 0}, {-1,-1}, {0, -1}, { 1, -1},
|
||||
};
|
||||
// Rune slot grid. Each slot gets a single rune centered
|
||||
// (with jitter) inside it.
|
||||
int slotR = gridSpacing / 2;
|
||||
uint32_t rng = static_cast<uint32_t>(gridSpacing) * 0x9E3779B9u +
|
||||
static_cast<uint32_t>(W) * 0x85EBCA6Bu;
|
||||
auto rngStep = [&]() {
|
||||
rng ^= rng << 13; rng ^= rng >> 17; rng ^= rng << 5;
|
||||
return rng;
|
||||
};
|
||||
int runeRadius = slotR / 3; // half-length of each stroke
|
||||
int jitterMax = slotR / 4;
|
||||
int runeCount = 0;
|
||||
for (int sy = slotR; sy < H + slotR; sy += gridSpacing) {
|
||||
for (int sx = slotR; sx < W + slotR; sx += gridSpacing) {
|
||||
// Per-rune jitter so the layout doesn't look like a
|
||||
// perfect grid; 5% of slots are skipped (empty
|
||||
// ground between runes).
|
||||
if ((rngStep() & 0xFF) < 13) continue; // ~5% skip
|
||||
int cx = sx + (static_cast<int>(rngStep() & 0xFF) - 128) *
|
||||
jitterMax / 128;
|
||||
int cy = sy + (static_cast<int>(rngStep() & 0xFF) - 128) *
|
||||
jitterMax / 128;
|
||||
// 3-5 strokes per rune.
|
||||
int strokeCount = 3 + (rngStep() % 3);
|
||||
for (int s = 0; s < strokeCount; ++s) {
|
||||
int dirA = rngStep() & 7;
|
||||
int dirB = rngStep() & 7;
|
||||
int lenA = runeRadius * (40 + static_cast<int>(rngStep() % 60)) / 100;
|
||||
int lenB = runeRadius * (40 + static_cast<int>(rngStep() % 60)) / 100;
|
||||
int x0 = cx + kDir[dirA][0] * lenA;
|
||||
int y0 = cy + kDir[dirA][1] * lenA;
|
||||
int x1 = cx + kDir[dirB][0] * lenB;
|
||||
int y1 = cy + kDir[dirB][1] * lenB;
|
||||
drawLine(x0, y0, x1, y1);
|
||||
}
|
||||
runeCount++;
|
||||
}
|
||||
}
|
||||
if (!stbi_write_png(outPath.c_str(), W, H, 3,
|
||||
pixels.data(), W * 3)) {
|
||||
std::fprintf(stderr,
|
||||
"gen-texture-runes: stbi_write_png failed for %s\n",
|
||||
outPath.c_str());
|
||||
return 1;
|
||||
}
|
||||
std::printf("Wrote %s\n", outPath.c_str());
|
||||
std::printf(" size : %dx%d\n", W, H);
|
||||
std::printf(" bg / rune : %s / %s\n", bgHex.c_str(), runeHex.c_str());
|
||||
std::printf(" runes : %d (slot %d px, 3-5 strokes each)\n",
|
||||
runeCount, gridSpacing);
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool handleGenTexture(int& i, int argc, char** argv, int& outRc) {
|
||||
|
|
@ -4316,6 +4434,9 @@ bool handleGenTexture(int& i, int argc, char** argv, int& outRc) {
|
|||
if (std::strcmp(argv[i], "--gen-texture-cracked") == 0 && i + 3 < argc) {
|
||||
outRc = handleCracked(i, argc, argv); return true;
|
||||
}
|
||||
if (std::strcmp(argv[i], "--gen-texture-runes") == 0 && i + 3 < argc) {
|
||||
outRc = handleRunes(i, argc, argv); return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -121,6 +121,8 @@ void printUsage(const char* argv0) {
|
|||
std::printf(" Honeycomb: hexagonal cells via Voronoi over a triangular seed lattice\n");
|
||||
std::printf(" --gen-texture-cracked <out.png> <bgHex> <crackHex> [seeds] [maxLength] [W H]\n");
|
||||
std::printf(" Cracked: branching random walks form fissures (mud / glass / dry earth)\n");
|
||||
std::printf(" --gen-texture-runes <out.png> <bgHex> <runeHex> [gridSpacing] [W H]\n");
|
||||
std::printf(" Runes: scattered angular glyphs of 3-5 strokes each (8 cardinal/diagonal angles)\n");
|
||||
std::printf(" --add-texture-to-zone <zoneDir> <png-path> [renameTo]\n");
|
||||
std::printf(" Copy an existing PNG into <zoneDir> (optionally renaming it on the way in)\n");
|
||||
std::printf(" --gen-mesh <wom-base> <cube|plane|sphere|cylinder|torus|cone|ramp> [size]\n");
|
||||
|
|
|
|||
|
|
@ -189,7 +189,7 @@ int main(int argc, char* argv[]) {
|
|||
"--gen-texture-parquet", "--gen-texture-bubbles",
|
||||
"--gen-texture-spider-web", "--gen-texture-gingham",
|
||||
"--gen-texture-lattice", "--gen-texture-honeycomb",
|
||||
"--gen-texture-cracked",
|
||||
"--gen-texture-cracked", "--gen-texture-runes",
|
||||
"--validate-glb", "--info-glb", "--info-glb-tree", "--info-glb-bytes",
|
||||
"--validate-jsondbc", "--check-glb-bounds", "--validate-stl",
|
||||
"--validate-png", "--validate-blp",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue