From 8fd6df6113aea4d429e914c4e27dd58522c20db3 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Sat, 9 May 2026 06:02:13 -0700 Subject: [PATCH] feat(editor): add --gen-texture-tartan plaid pattern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 3-color crossing-band pattern: 6-band repeat sequence (A A B C C B) per axis, with the pixel color at any position being the average of the horizontal-band and vertical-band colors. That averaging at intersections produces the characteristic diamond grid of Scottish tartans without explicit "weave" math — the band overlap pattern just falls out. Defaults: bandPx=32 (repeat=192px). Useful for clan banners, kilts, blanket textures, fabric set dressing. Brings the procedural texture pattern set to 33. --- tools/editor/cli_gen_texture.cpp | 94 ++++++++++++++++++++++++++++++++ tools/editor/cli_help.cpp | 2 + tools/editor/main.cpp | 2 +- 3 files changed, 97 insertions(+), 1 deletion(-) diff --git a/tools/editor/cli_gen_texture.cpp b/tools/editor/cli_gen_texture.cpp index 22687d50..bc87f324 100644 --- a/tools/editor/cli_gen_texture.cpp +++ b/tools/editor/cli_gen_texture.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include // stb_image_write impl lives in texture_exporter.cpp; @@ -2789,6 +2790,96 @@ int handleFlame(int& i, int argc, char** argv) { return 0; } +int handleTartan(int& i, int argc, char** argv) { + // Tartan plaid: 3-color crossing band pattern. Each cell + // belongs to one of 6 logical zones (3 vertical + 3 + // horizontal bands per repeat unit) and the displayed + // color is the additive mix of the band's vertical and + // horizontal contributions — produces the characteristic + // overlap diamond grid of Scottish tartans. + std::string outPath = argv[++i]; + std::string aHex = argv[++i]; + std::string bHex = argv[++i]; + std::string cHex = argv[++i]; + int bandPx = 32; + int W = 256, H = 256; + if (i + 1 < argc && argv[i + 1][0] != '-') { + try { bandPx = 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 || + bandPx < 4 || bandPx > 256) { + std::fprintf(stderr, + "gen-texture-tartan: invalid dims (W/H 1..8192, bandPx 4..256)\n"); + return 1; + } + uint8_t ar, ag, ab, br, bg, bb_, cr_, cg_, cb_; + if (!parseHex(aHex, ar, ag, ab) || + !parseHex(bHex, br, bg, bb_) || + !parseHex(cHex, cr_, cg_, cb_)) { + std::fprintf(stderr, + "gen-texture-tartan: one of the hex colors is invalid\n"); + return 1; + } + // 3-band repeat: A wide, B narrow, C medium. Repeat is + // 6 × bandPx wide. Each band weight is constant within + // its slice; the displayed pixel color is averaged from + // the vertical band (column) and horizontal band (row). + auto bandColor = [&](int t) -> std::tuple { + // t is position modulo (6 * bandPx). Map to one of A/B/C + // based on which segment t falls in. + int slice = (t / bandPx) % 6; + // 6-slice repeat pattern: A A B C C B (gives a typical + // tartan look — wide A blocks separated by thin B/C lines). + switch (slice) { + case 0: case 1: return {ar, ag, ab}; + case 2: return {br, bg, bb_}; + case 3: case 4: return {cr_, cg_, cb_}; + default: return {br, bg, bb_}; + } + }; + int repeat = 6 * bandPx; + std::vector pixels(static_cast(W) * H * 3, 0); + for (int y = 0; y < H; ++y) { + int yMod = ((y % repeat) + repeat) % repeat; + auto [hr, hg, hb] = bandColor(yMod); + for (int x = 0; x < W; ++x) { + int xMod = ((x % repeat) + repeat) % repeat; + auto [vr, vg, vb] = bandColor(xMod); + // Average the horizontal-band and vertical-band + // colors. At intersections the average produces a + // distinct mid-tone that creates the diamond grid + // characteristic of plaid. + uint8_t r = static_cast((hr + vr) / 2); + uint8_t g = static_cast((hg + vg) / 2); + uint8_t b = static_cast((hb + vb) / 2); + size_t i2 = (static_cast(y) * W + x) * 3; + pixels[i2 + 0] = r; + pixels[i2 + 1] = g; + pixels[i2 + 2] = b; + } + } + if (!stbi_write_png(outPath.c_str(), W, H, 3, + pixels.data(), W * 3)) { + std::fprintf(stderr, + "gen-texture-tartan: 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(" colors A/B/C: %s / %s / %s\n", + aHex.c_str(), bHex.c_str(), cHex.c_str()); + std::printf(" band px : %d (repeat %d px)\n", + bandPx, repeat); + return 0; +} + } // namespace bool handleGenTexture(int& i, int argc, char** argv, int& outRc) { @@ -2881,6 +2972,9 @@ bool handleGenTexture(int& i, int argc, char** argv, int& outRc) { if (std::strcmp(argv[i], "--gen-texture-flame") == 0 && i + 3 < argc) { outRc = handleFlame(i, argc, argv); return true; } + if (std::strcmp(argv[i], "--gen-texture-tartan") == 0 && i + 4 < argc) { + outRc = handleTartan(i, argc, argv); return true; + } return false; } diff --git a/tools/editor/cli_help.cpp b/tools/editor/cli_help.cpp index b0bb7d7a..51170561 100644 --- a/tools/editor/cli_help.cpp +++ b/tools/editor/cli_help.cpp @@ -93,6 +93,8 @@ void printUsage(const char* argv0) { std::printf(" Coral reef: branching tree shapes via random angle walks (default 12 branches)\n"); std::printf(" --gen-texture-flame [seed] [W H]\n"); std::printf(" Flame: vertical color gradient from dark base to hot top with noise flicker\n"); + std::printf(" --gen-texture-tartan [bandPx] [W H]\n"); + std::printf(" Tartan plaid: 3-color crossing bands forming Scottish-style overlap pattern\n"); std::printf(" --add-texture-to-zone [renameTo]\n"); std::printf(" Copy an existing PNG into (optionally renaming it on the way in)\n"); std::printf(" --gen-mesh [size]\n"); diff --git a/tools/editor/main.cpp b/tools/editor/main.cpp index 0965f187..85180c35 100644 --- a/tools/editor/main.cpp +++ b/tools/editor/main.cpp @@ -141,7 +141,7 @@ int main(int argc, char* argv[]) { "--gen-texture-lava", "--gen-texture-tile", "--gen-texture-bark", "--gen-texture-clouds", "--gen-texture-stars", "--gen-texture-vines", "--gen-texture-mosaic", "--gen-texture-rust", "--gen-texture-circuit", - "--gen-texture-coral", "--gen-texture-flame", + "--gen-texture-coral", "--gen-texture-flame", "--gen-texture-tartan", "--validate-glb", "--info-glb", "--info-glb-tree", "--info-glb-bytes", "--validate-jsondbc", "--check-glb-bounds", "--validate-stl", "--validate-png", "--validate-blp",