From 83a6992ae0edf6c62ae468b2555959301aa12ba0 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Sat, 9 May 2026 08:14:04 -0700 Subject: [PATCH] feat(editor): add --gen-texture-spider-web radial-ring pattern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 39th procedural texture: classic geometric spider web — N radial spokes plus M concentric polygonal rings centered on the image. Spoke pixels are detected by angular distance to the nearest spoke scaled by radius, so spokes stay constant pixel width regardless of how far they reach. Useful for haunted house decals, dungeon corner overlays, witch-hut interiors, magical-trap ground markers. Defaults to 8 spokes (every 45 deg) and 5 evenly-spaced rings. --- tools/editor/cli_gen_texture.cpp | 106 +++++++++++++++++++++++++++++++ tools/editor/cli_help.cpp | 2 + tools/editor/main.cpp | 1 + 3 files changed, 109 insertions(+) diff --git a/tools/editor/cli_gen_texture.cpp b/tools/editor/cli_gen_texture.cpp index b8cba8d7..bfb9c390 100644 --- a/tools/editor/cli_gen_texture.cpp +++ b/tools/editor/cli_gen_texture.cpp @@ -3707,6 +3707,109 @@ int handleBubbles(int& i, int argc, char** argv) { return 0; } +int handleSpiderWeb(int& i, int argc, char** argv) { + // Spider web: classic geometric web with N radial spokes + // and M concentric polygonal rings centered on the image. + // Spokes are detected by angular distance to the nearest + // multiple of 2pi/N (scaled by radius so spokes are pixel- + // wide near the center and stay readable far out). Rings + // are detected by radial distance to the nearest of M + // evenly-spaced radii. + std::string outPath = argv[++i]; + std::string bgHex = argv[++i]; + std::string webHex = argv[++i]; + int spokes = 8; + int rings = 5; + int W = 256, H = 256; + if (i + 1 < argc && argv[i + 1][0] != '-') { + try { spokes = std::stoi(argv[++i]); } catch (...) {} + } + if (i + 1 < argc && argv[i + 1][0] != '-') { + try { rings = 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 || + spokes < 3 || spokes > 64 || + rings < 1 || rings > 32) { + std::fprintf(stderr, + "gen-texture-spider-web: invalid dims (W/H 1..8192, spokes 3..64, rings 1..32)\n"); + return 1; + } + uint8_t br_, bg_, bb_, wr, wg, wb_; + if (!parseHex(bgHex, br_, bg_, bb_) || + !parseHex(webHex, wr, wg, wb_)) { + std::fprintf(stderr, + "gen-texture-spider-web: bg or web hex color is invalid\n"); + return 1; + } + constexpr float kPi = 3.14159265358979323846f; + const float cx = W * 0.5f; + const float cy = H * 0.5f; + // Web extends to the smaller half-extent so it always fits. + const float maxR = std::min(cx, cy); + const float spokeStep = 2.0f * kPi / spokes; + const float ringStep = maxR / rings; + // Line widths in pixels — kept fixed so the web reads at any + // image size; users wanting a denser/thicker web can re-run + // with bigger spoke/ring counts. + const float lineHalfW = 1.0f; + std::vector pixels(static_cast(W) * H * 3, 0); + for (int y = 0; y < H; ++y) { + for (int x = 0; x < W; ++x) { + float dx = x + 0.5f - cx; + float dy = y + 0.5f - cy; + float r = std::sqrt(dx * dx + dy * dy); + uint8_t cr_, cg_, cb_; + cr_ = br_; cg_ = bg_; cb_ = bb_; + if (r > 0.5f && r < maxR + lineHalfW) { + // Spoke check: angular distance to nearest spoke + // line, measured as arc length (= r * dTheta) so + // spokes have constant pixel width regardless of r. + float theta = std::atan2(dy, dx); + float wrapped = std::fmod(theta + kPi * 100.0f, spokeStep); + float spokeDelta = std::min(wrapped, spokeStep - wrapped); + float arcDist = spokeDelta * r; + if (arcDist <= lineHalfW) { + cr_ = wr; cg_ = wg; cb_ = wb_; + } + // Ring check: nearest ring radius. Skip the + // would-be ring at r=0 (which is the center). + float ringIdx = r / ringStep; + float nearestRing = std::round(ringIdx) * ringStep; + if (nearestRing > 0.5f && + std::fabs(r - nearestRing) <= lineHalfW) { + cr_ = wr; cg_ = wg; cb_ = wb_; + } + } + size_t idx = (static_cast(y) * W + x) * 3; + pixels[idx + 0] = cr_; + pixels[idx + 1] = cg_; + pixels[idx + 2] = cb_; + } + } + if (!stbi_write_png(outPath.c_str(), W, H, 3, + pixels.data(), W * 3)) { + std::fprintf(stderr, + "gen-texture-spider-web: 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 (web center at %.1f, %.1f)\n", + W, H, cx, cy); + std::printf(" bg / web : %s / %s\n", bgHex.c_str(), webHex.c_str()); + std::printf(" spokes : %d (every %.1f°)\n", + spokes, 360.0f / spokes); + std::printf(" rings : %d (spacing %.1f px)\n", + rings, ringStep); + return 0; +} + } // namespace bool handleGenTexture(int& i, int argc, char** argv, int& outRc) { @@ -3826,6 +3929,9 @@ bool handleGenTexture(int& i, int argc, char** argv, int& outRc) { if (std::strcmp(argv[i], "--gen-texture-bubbles") == 0 && i + 4 < argc) { outRc = handleBubbles(i, argc, argv); return true; } + if (std::strcmp(argv[i], "--gen-texture-spider-web") == 0 && i + 3 < argc) { + outRc = handleSpiderWeb(i, argc, argv); return true; + } return false; } diff --git a/tools/editor/cli_help.cpp b/tools/editor/cli_help.cpp index a6326907..09932838 100644 --- a/tools/editor/cli_help.cpp +++ b/tools/editor/cli_help.cpp @@ -111,6 +111,8 @@ void printUsage(const char* argv0) { std::printf(" Parquet: basket-weave wood floor pattern with checkered horizontal/vertical plank pairs\n"); std::printf(" --gen-texture-bubbles [count] [minR] [maxR] [rimW] [W H]\n"); std::printf(" Bubbles: scattered overlapping circles with bright rims (foam, water, magic)\n"); + std::printf(" --gen-texture-spider-web [spokes] [rings] [W H]\n"); + std::printf(" Spider web: N radial spokes + M concentric polygonal rings centered on the image\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 041fa05f..1277b542 100644 --- a/tools/editor/main.cpp +++ b/tools/editor/main.cpp @@ -166,6 +166,7 @@ int main(int argc, char* argv[]) { "--gen-texture-scales", "--gen-texture-stained-glass", "--gen-texture-shingles", "--gen-texture-frost", "--gen-texture-parquet", "--gen-texture-bubbles", + "--gen-texture-spider-web", "--validate-glb", "--info-glb", "--info-glb-tree", "--info-glb-bytes", "--validate-jsondbc", "--check-glb-bounds", "--validate-stl", "--validate-png", "--validate-blp",