From c1f3d5aba49dff1925e9e8f1028bdff82b2dd2d1 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Sat, 9 May 2026 06:49:29 -0700 Subject: [PATCH] feat(editor): add --gen-texture-herringbone chevron pattern 32nd procedural texture: classic V-shaped herringbone done as horizontal strips of parallel slanted lines whose slant direction flips every strip. Implementation is a per-pixel shear: shifting x by the row's local-y collapses each diagonal into a vertical band in shifted-x space, so a single modulo picks line vs background. Useful for parquet floors, brick patios, fabric weaves, fish scale-style chain mail, anywhere needing a strong directional pattern. Two-color, defaults to 32-px strips with 12-px line spacing and 4-px line width. --- tools/editor/cli_gen_texture.cpp | 90 ++++++++++++++++++++++++++++++++ tools/editor/cli_help.cpp | 2 + tools/editor/main.cpp | 2 +- 3 files changed, 93 insertions(+), 1 deletion(-) diff --git a/tools/editor/cli_gen_texture.cpp b/tools/editor/cli_gen_texture.cpp index a10e2990..d5193589 100644 --- a/tools/editor/cli_gen_texture.cpp +++ b/tools/editor/cli_gen_texture.cpp @@ -2971,6 +2971,93 @@ int handleArgyle(int& i, int argc, char** argv) { return 0; } +int handleHerringbone(int& i, int argc, char** argv) { + // Herringbone (chevron-style): horizontal strips of slanted + // parallel lines whose slant direction flips every strip, + // producing the V-shaped "fish bone" pattern that's the + // hallmark of herringbone fabric and parquet flooring. + // Implemented as a per-pixel shear: shifting x by the row's + // local-y collapses each diagonal line into a vertical band + // in shifted-x space, where modular arithmetic picks line vs + // background. + std::string outPath = argv[++i]; + std::string bgHex = argv[++i]; + std::string lineHex = argv[++i]; + int stripHeight = 32; // height of each constant-direction strip + int lineSpacing = 12; // distance between adjacent lines along x + int lineWidth = 4; // line thickness in shifted-x coords + int W = 256, H = 256; + if (i + 1 < argc && argv[i + 1][0] != '-') { + try { stripHeight = std::stoi(argv[++i]); } catch (...) {} + } + if (i + 1 < argc && argv[i + 1][0] != '-') { + try { lineSpacing = std::stoi(argv[++i]); } catch (...) {} + } + if (i + 1 < argc && argv[i + 1][0] != '-') { + try { lineWidth = 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 || + stripHeight < 4 || stripHeight > 256 || + lineSpacing < 4 || lineSpacing > 256 || + lineWidth < 1 || lineWidth >= lineSpacing) { + std::fprintf(stderr, + "gen-texture-herringbone: invalid dims (W/H 1..8192, stripH 4..256, " + "spacing 4..256, lineW 1..spacing-1)\n"); + return 1; + } + uint8_t br_, bg_, bb_, lr_, lg_, lb_; + if (!parseHex(bgHex, br_, bg_, bb_) || + !parseHex(lineHex, lr_, lg_, lb_)) { + std::fprintf(stderr, + "gen-texture-herringbone: bg or line hex color is invalid\n"); + return 1; + } + std::vector pixels(static_cast(W) * H * 3, 0); + for (int y = 0; y < H; ++y) { + int rowOfStrips = y / stripHeight; + int withinStrip = y - rowOfStrips * stripHeight; + // Even strips: lines slant down-right (+45°-ish, scaled by + // stripHeight/lineSpacing). Odd strips: slant down-left. + int sign = (rowOfStrips & 1) ? -1 : 1; + for (int x = 0; x < W; ++x) { + // Shift x by ±withinStrip — collapses the slanted line + // into a vertical strip in shifted-x coords. + int shifted = x + sign * withinStrip; + int phase = ((shifted % lineSpacing) + lineSpacing) % lineSpacing; + uint8_t r, g, b; + if (phase < lineWidth) { + r = lr_; g = lg_; b = lb_; + } else { + r = br_; g = bg_; b = bb_; + } + size_t idx = (static_cast(y) * W + x) * 3; + pixels[idx + 0] = r; + pixels[idx + 1] = g; + pixels[idx + 2] = b; + } + } + if (!stbi_write_png(outPath.c_str(), W, H, 3, + pixels.data(), W * 3)) { + std::fprintf(stderr, + "gen-texture-herringbone: 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 / line : %s / %s\n", bgHex.c_str(), lineHex.c_str()); + std::printf(" strip H : %d (slant flips per strip)\n", stripHeight); + std::printf(" line : width %d / spacing %d\n", + lineWidth, lineSpacing); + return 0; +} + } // namespace bool handleGenTexture(int& i, int argc, char** argv, int& outRc) { @@ -3069,6 +3156,9 @@ bool handleGenTexture(int& i, int argc, char** argv, int& outRc) { if (std::strcmp(argv[i], "--gen-texture-argyle") == 0 && i + 4 < argc) { outRc = handleArgyle(i, argc, argv); return true; } + if (std::strcmp(argv[i], "--gen-texture-herringbone") == 0 && i + 3 < argc) { + outRc = handleHerringbone(i, argc, argv); return true; + } return false; } diff --git a/tools/editor/cli_help.cpp b/tools/editor/cli_help.cpp index efd31971..6c28c47c 100644 --- a/tools/editor/cli_help.cpp +++ b/tools/editor/cli_help.cpp @@ -97,6 +97,8 @@ void printUsage(const char* argv0) { std::printf(" Tartan plaid: 3-color crossing bands forming Scottish-style overlap pattern\n"); std::printf(" --gen-texture-argyle [cellPx] [W H]\n"); std::printf(" Argyle: 45-rotated lozenge checkerboard with diagonal stitch lines (sweater knit)\n"); + std::printf(" --gen-texture-herringbone [stripH] [lineSpacing] [lineWidth] [W H]\n"); + std::printf(" Herringbone: slanted parallel lines that flip direction every strip (chevron / fish-bone)\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 bc981034..f4c615b9 100644 --- a/tools/editor/main.cpp +++ b/tools/editor/main.cpp @@ -146,7 +146,7 @@ int main(int argc, char* argv[]) { "--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-tartan", - "--gen-texture-argyle", + "--gen-texture-argyle", "--gen-texture-herringbone", "--validate-glb", "--info-glb", "--info-glb-tree", "--info-glb-bytes", "--validate-jsondbc", "--check-glb-bounds", "--validate-stl", "--validate-png", "--validate-blp",