From 2a5198df4280598dbe3568508b6c9a6be4305289 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Sat, 9 May 2026 13:14:18 -0700 Subject: [PATCH] feat(editor): add --gen-texture-ironbark hardwood-plate pattern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 71st procedural texture: vertical wood-grain streaks (like --gen-texture-bark) overlaid with horizontal "plate" bands at regular Y intervals — the segmented look of mature hardwood / ironwood / sycamore bark. Per-pixel logic: • horizontal crack: top crackW pixels of every plateY cycle gets the dark crack color • vertical streak: per-streak hash determines an offset crack position within each streakSpacing block • elsewhere: base color modulated by per-streak-and-plate brightness jitter (±15) so the surface has natural texture variation Distinct from --gen-texture-bark (vertical-crack only) and --gen-texture-wood (vertical-streak only). Useful for ancient trees, druid grove deadwood, dwarven hardwood beams, fortress door planks, totem-pole carvings. --- tools/editor/cli_arg_required.cpp | 1 + tools/editor/cli_gen_texture.cpp | 86 +++++++++++++++++++++++++++++++ tools/editor/cli_help.cpp | 2 + 3 files changed, 89 insertions(+) diff --git a/tools/editor/cli_arg_required.cpp b/tools/editor/cli_arg_required.cpp index 6eddad2e..9225dc99 100644 --- a/tools/editor/cli_arg_required.cpp +++ b/tools/editor/cli_arg_required.cpp @@ -107,6 +107,7 @@ const char* const kArgRequired[] = { "--gen-texture-plaid", "--gen-texture-diamond-grid", "--gen-texture-houndstooth", "--gen-texture-chevron", "--gen-texture-dunes", "--gen-texture-swirl", + "--gen-texture-ironbark", "--validate-glb", "--info-glb", "--info-glb-tree", "--info-glb-bytes", "--validate-jsondbc", "--check-glb-bounds", "--validate-stl", "--validate-png", "--validate-blp", diff --git a/tools/editor/cli_gen_texture.cpp b/tools/editor/cli_gen_texture.cpp index 27a49d29..8d88d521 100644 --- a/tools/editor/cli_gen_texture.cpp +++ b/tools/editor/cli_gen_texture.cpp @@ -3500,6 +3500,91 @@ int handleKnit(int& i, int argc, char** argv) { return 0; } +int handleIronbark(int& i, int argc, char** argv) { + // Ironbark: vertical wood-grain streaks (like --gen-texture- + // bark) overlaid with horizontal "plate" bands at regular + // intervals — the segmented look of mature hardwood / iron- + // wood / sycamore bark. Within each plate cell, the inside + // is the base wood color and the cell border is the dark + // crack color. Distinct from --gen-texture-bark (vertical- + // crack only) and --gen-texture-wood (vertical-streak only). + std::string outPath = argv[++i]; + std::string baseHex = argv[++i]; + std::string crackHex = argv[++i]; + int streakSpacing = 14; // horizontal pitch of vertical streaks + int plateY = 48; // vertical pitch of horizontal plate bands + int crackW = 1; // crack thickness + uint32_t seed = 1; + int W = 256, H = 256; + parseOptInt(i, argc, argv, streakSpacing); + parseOptInt(i, argc, argv, plateY); + parseOptInt(i, argc, argv, crackW); + parseOptUint(i, argc, argv, seed); + parseOptInt(i, argc, argv, W); + parseOptInt(i, argc, argv, H); + if (W < 1 || H < 1 || W > 8192 || H > 8192 || + streakSpacing < 4 || streakSpacing > 1024 || + plateY < 8 || plateY > 4096 || + crackW < 1 || crackW * 4 >= std::min(streakSpacing, plateY)) { + std::fprintf(stderr, + "gen-texture-ironbark: invalid dims (W/H 1..8192, " + "streakSpacing 4..1024, plateY 8..4096, crackW 1..min/4)\n"); + return 1; + } + uint8_t lr, lg, lb_, dr, dg, db; + if (!parseHexOrError(baseHex, lr, lg, lb_, + "gen-texture-ironbark")) return 1; + if (!parseHexOrError(crackHex, dr, dg, db, + "gen-texture-ironbark")) return 1; + auto hash32 = [](uint32_t x) -> uint32_t { + x ^= x >> 16; x *= 0x7feb352d; + x ^= x >> 15; x *= 0x846ca68b; + x ^= x >> 16; return x; + }; + auto clampU8 = [](int v) { + return v < 0 ? 0 : (v > 255 ? 255 : v); + }; + std::vector pixels(static_cast(W) * H * 3, 0); + for (int y = 0; y < H; ++y) { + // Plate-cell row index + within-plate Y offset. + int plateRow = y / plateY; + int yInPlate = y - plateRow * plateY; + // Horizontal crack: top crackW pixels of every plate. + bool onPlateCrack = (yInPlate < crackW); + for (int x = 0; x < W; ++x) { + // Vertical streak: hash-jittered crack position per + // streakSpacing column. + int streakCol = x / streakSpacing; + int xInStreak = x - streakCol * streakSpacing; + // Per-streak hash determines exact crack offset within + // the streakSpacing block, AND a per-streak-+-plateRow + // brightness tint. + uint32_t hStreak = hash32(streakCol * 0x9E3779B1u + seed); + int crackOffset = static_cast(hStreak % streakSpacing); + bool onStreakCrack = std::abs(xInStreak - crackOffset) < crackW; + int tint = (static_cast(hash32( + streakCol * 0x9E3779B1u + plateRow * 0x85EBCA77u + seed) + % 31)) - 15; // -15..+15 brightness jitter + uint8_t r, g, b; + if (onPlateCrack || onStreakCrack) { + r = dr; g = dg; b = db; + } else { + r = static_cast(clampU8(lr + tint)); + g = static_cast(clampU8(lg + tint)); + b = static_cast(clampU8(lb_ + tint)); + } + setPixelRGB(pixels, W, x, y, r, g, b); + } + } + if (!savePngOrError(outPath, W, H, pixels, + "gen-texture-ironbark")) return 1; + printPngWrote(outPath, W, H); + std::printf(" base/crack : %s / %s\n", baseHex.c_str(), crackHex.c_str()); + std::printf(" bark : streak=%d, plateY=%d, crackW=%d (seed %u)\n", + streakSpacing, plateY, crackW, seed); + return 0; +} + int handleSwirl(int& i, int argc, char** argv) { // Logarithmic spiral: pixels are colored as the spiral arm // when (θ - log(r) * spiralFactor) mod 2π/N falls inside a @@ -5145,6 +5230,7 @@ constexpr TextureEntry kTextureTable[] = { {"--gen-texture-chevron", 3, handleChevron}, {"--gen-texture-dunes", 3, handleDunes}, {"--gen-texture-swirl", 3, handleSwirl}, + {"--gen-texture-ironbark", 3, handleIronbark}, }; } // namespace diff --git a/tools/editor/cli_help.cpp b/tools/editor/cli_help.cpp index 3f8af045..32810ead 100644 --- a/tools/editor/cli_help.cpp +++ b/tools/editor/cli_help.cpp @@ -175,6 +175,8 @@ void printUsage(const char* argv0) { std::printf(" Dunes: stack of parallel sinusoidal curves (desert ground / shallow-water sand / wave ripples)\n"); std::printf(" --gen-texture-swirl [armCount] [spiralFactor] [armWidth] [W H]\n"); std::printf(" Swirl: N-arm logarithmic spiral (magic sigils / summoning circles / ritual floor markings)\n"); + std::printf(" --gen-texture-ironbark [streakSpacing] [plateY] [crackW] [seed] [W H]\n"); + std::printf(" Ironbark: vertical wood streaks + horizontal plate bands (mature hardwood / sycamore / ironwood)\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");