diff --git a/tools/editor/cli_gen_texture.cpp b/tools/editor/cli_gen_texture.cpp index 7d5b785f..2832e2c3 100644 --- a/tools/editor/cli_gen_texture.cpp +++ b/tools/editor/cli_gen_texture.cpp @@ -2580,6 +2580,126 @@ int handleCircuit(int& i, int argc, char** argv) { return 0; } +int handleCoral(int& i, int argc, char** argv) { + // Coral reef: water-color background plus N branching tree + // shapes that grow from random anchor points. Each branch + // walks a curved path (random angle drift), splitting into + // 2-3 sub-branches at random intervals so the result reads + // as organic coral rather than straight lines. + std::string outPath = argv[++i]; + std::string waterHex = argv[++i]; + std::string coralHex = argv[++i]; + uint32_t seed = 1; + int branchCount = 12; + int W = 256, H = 256; + if (i + 1 < argc && argv[i + 1][0] != '-') { + try { seed = static_cast(std::stoul(argv[++i])); } catch (...) {} + } + if (i + 1 < argc && argv[i + 1][0] != '-') { + try { branchCount = 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 || + branchCount < 0 || branchCount > 1024) { + std::fprintf(stderr, + "gen-texture-coral: invalid dims (W/H 1..8192, branchCount 0..1024)\n"); + return 1; + } + uint8_t wr, wg, wb_, cr, cg, cb; + if (!parseHex(waterHex, wr, wg, wb_)) { + std::fprintf(stderr, + "gen-texture-coral: '%s' is not a valid hex color\n", + waterHex.c_str()); + return 1; + } + if (!parseHex(coralHex, cr, cg, cb)) { + std::fprintf(stderr, + "gen-texture-coral: '%s' is not a valid hex color\n", + coralHex.c_str()); + return 1; + } + uint32_t state = seed ? seed : 1u; + auto next01 = [&state]() -> float { + state = state * 1664525u + 1013904223u; + return (state >> 8) * (1.0f / 16777216.0f); + }; + std::vector pixels(static_cast(W) * H * 3, 0); + for (int p = 0; p < W * H; ++p) { + size_t i2 = static_cast(p) * 3; + pixels[i2 + 0] = wr; pixels[i2 + 1] = wg; pixels[i2 + 2] = wb_; + } + auto setPx = [&](int x, int y) { + if (x < 0 || y < 0 || x >= W || y >= H) return; + size_t i2 = (static_cast(y) * W + x) * 3; + pixels[i2 + 0] = cr; pixels[i2 + 1] = cg; pixels[i2 + 2] = cb; + }; + // Recursive branch growth via explicit stack (no real + // recursion to avoid blowing through stack for deep splits). + struct Branch { float x, y, angle, length, thickness; }; + std::vector stack; + int totalBranches = 0; + for (int b = 0; b < branchCount; ++b) { + // Anchor at the bottom edge, growing upward + Branch root; + root.x = next01() * W; + root.y = H - 1; + root.angle = -3.14159f * 0.5f + (next01() - 0.5f) * 0.6f; + root.length = 30 + next01() * 40; + root.thickness = 2.0f; + stack.push_back(root); + while (!stack.empty()) { + Branch br = stack.back(); + stack.pop_back(); + ++totalBranches; + float x = br.x, y = br.y; + int steps = static_cast(br.length); + for (int s = 0; s < steps; ++s) { + // Random walk + slight upward bias + br.angle += (next01() - 0.5f) * 0.15f; + x += std::cos(br.angle); + y += std::sin(br.angle); + int rad = static_cast(std::ceil(br.thickness)); + for (int dy = -rad; dy <= rad; ++dy) { + for (int dx = -rad; dx <= rad; ++dx) { + if (dx*dx + dy*dy > rad*rad) continue; + setPx(static_cast(x) + dx, + static_cast(y) + dy); + } + } + // Split occasionally + if (next01() < 0.05f && br.thickness > 1.0f) { + Branch child; + child.x = x; child.y = y; + child.angle = br.angle + (next01() - 0.5f) * 1.2f; + child.length = br.length * (0.4f + next01() * 0.3f); + child.thickness = br.thickness * 0.7f; + if (stack.size() < 256) stack.push_back(child); + } + } + } + } + if (!stbi_write_png(outPath.c_str(), W, H, 3, + pixels.data(), W * 3)) { + std::fprintf(stderr, + "gen-texture-coral: 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(" water/coral : %s / %s\n", + waterHex.c_str(), coralHex.c_str()); + std::printf(" branches : %d roots → %d total (with splits)\n", + branchCount, totalBranches); + std::printf(" seed : %u\n", seed); + return 0; +} + } // namespace bool handleGenTexture(int& i, int argc, char** argv, int& outRc) { @@ -2666,6 +2786,9 @@ bool handleGenTexture(int& i, int argc, char** argv, int& outRc) { if (std::strcmp(argv[i], "--gen-texture-circuit") == 0 && i + 3 < argc) { outRc = handleCircuit(i, argc, argv); return true; } + if (std::strcmp(argv[i], "--gen-texture-coral") == 0 && i + 3 < argc) { + outRc = handleCoral(i, argc, argv); return true; + } return false; } diff --git a/tools/editor/cli_help.cpp b/tools/editor/cli_help.cpp index af5f8d91..379f84ad 100644 --- a/tools/editor/cli_help.cpp +++ b/tools/editor/cli_help.cpp @@ -89,6 +89,8 @@ void printUsage(const char* argv0) { std::printf(" Metal with rust patches: noise blob threshold + per-pixel grain (default coverage=0.4)\n"); std::printf(" --gen-texture-circuit [seed] [traceCount] [W H]\n"); std::printf(" Sci-fi PCB pattern: orthogonal traces with right-angle turns + via dots (default 24 traces)\n"); + std::printf(" --gen-texture-coral [seed] [branchCount] [W H]\n"); + std::printf(" Coral reef: branching tree shapes via random angle walks (default 12 branches)\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 b41fe538..4b89ff95 100644 --- a/tools/editor/main.cpp +++ b/tools/editor/main.cpp @@ -222,6 +222,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", "--validate-glb", "--info-glb", "--info-glb-tree", "--info-glb-bytes", "--validate-jsondbc", "--check-glb-bounds", "--validate-stl", "--validate-png", "--validate-blp",