diff --git a/util/noise/noisegui.cpp b/util/noise/noisegui.cpp new file mode 100644 index 0000000..a9ee4d8 --- /dev/null +++ b/util/noise/noisegui.cpp @@ -0,0 +1,403 @@ +// noisegui.cpp +#include "pnoise.hpp" +#include "../bmpwriter.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// Convert noise value to grayscale color +Vec3 noiseToColor(double noiseValue) { + float value = static_cast(noiseValue); + return Vec3(value, value, value); +} + +// Convert noise value to color using a blue-to-red colormap +Vec3 noiseToHeatmap(double noiseValue) { + float value = static_cast(noiseValue); + + if (value < 0.25f) { + float t = value / 0.25f; + return Vec3(0.0f, t, 1.0f); + } else if (value < 0.5f) { + float t = (value - 0.25f) / 0.25f; + return Vec3(0.0f, 1.0f, 1.0f - t); + } else if (value < 0.75f) { + float t = (value - 0.5f) / 0.25f; + return Vec3(t, 1.0f, 0.0f); + } else { + float t = (value - 0.75f) / 0.25f; + return Vec3(1.0f, 1.0f - t, 0.0f); + } +} + +// Convert noise value to terrain-like colors +Vec3 noiseToTerrain(double noiseValue) { + float value = static_cast(noiseValue); + + if (value < 0.3f) { + return Vec3(0.0f, 0.0f, 0.3f + value * 0.4f); + } else if (value < 0.4f) { + return Vec3(0.76f, 0.70f, 0.50f); + } else if (value < 0.6f) { + float t = (value - 0.4f) / 0.2f; + return Vec3(0.0f, 0.4f + t * 0.3f, 0.0f); + } else if (value < 0.8f) { + return Vec3(0.0f, 0.3f, 0.0f); + } else { + float t = (value - 0.8f) / 0.2f; + return Vec3(0.8f + t * 0.2f, 0.8f + t * 0.2f, 0.8f + t * 0.2f); + } +} + +class NoiseTexture { +private: + GLuint textureID; + int width, height; + std::vector pixelData; + +public: + NoiseTexture(int w, int h) : width(w), height(h) { + pixelData.resize(width * height * 3); + glGenTextures(1, &textureID); + updateTexture(); + } + + ~NoiseTexture() { + glDeleteTextures(1, &textureID); + } + + void generateNoise(const PerlinNoise& pn, double scale, int octaves, + const std::string& noiseType, const std::string& colorMap) { + for (int y = 0; y < height; ++y) { + for (int x = 0; x < width; ++x) { + double noise = 0.0; + + if (noiseType == "Basic") { + noise = pn.noise(x * scale, y * scale); + } else if (noiseType == "FBM") { + noise = pn.fractal(octaves, x * scale, y * scale); + } else if (noiseType == "Turbulence") { + noise = pn.turbulence(octaves, x * scale, y * scale); + } else if (noiseType == "Ridged") { + noise = pn.ridgedMultiFractal(octaves, x * scale, y * scale, 0.0, 2.0, 0.5, 1.0); + } + + Vec3 color; + if (colorMap == "Grayscale") { + color = noiseToColor(noise); + } else if (colorMap == "Heatmap") { + color = noiseToHeatmap(noise); + } else if (colorMap == "Terrain") { + color = noiseToTerrain(noise); + } + + int index = (y * width + x) * 3; + pixelData[index] = static_cast(color.x * 255); + pixelData[index + 1] = static_cast(color.y * 255); + pixelData[index + 2] = static_cast(color.z * 255); + } + } + updateTexture(); + } + + void updateTexture() { + glBindTexture(GL_TEXTURE_2D, textureID); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, pixelData.data()); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } + + void draw(const char* label, const ImVec2& size) { + ImGui::Image((void*)(intptr_t)textureID, size); + ImGui::Text("%s", label); + } + + GLuint getTextureID() const { return textureID; } +}; + +class NoiseComparisonApp { +private: + GLFWwindow* window; + int windowWidth, windowHeight; + + // Noise parameters + double scale; + int octaves; + unsigned int seed; + std::string noiseType; + std::string colorMap; + + // Comparison views + struct ComparisonView { + std::unique_ptr texture; + double scale; + int octaves; + unsigned int seed; + std::string noiseType; + std::string colorMap; + std::string label; + }; + + std::vector views; + int textureSize; + + // Preset management + struct Preset { + std::string name; + double scale; + int octaves; + std::string noiseType; + std::string colorMap; + }; + + std::vector presets; + int selectedPreset; + +public: + NoiseComparisonApp() : windowWidth(1400), windowHeight(900), scale(0.01), octaves(4), + seed(42), noiseType("FBM"), colorMap("Grayscale"), textureSize(256) { + initializePresets(); + } + + bool initialize() { + if (!glfwInit()) return false; + + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + + window = glfwCreateWindow(windowWidth, windowHeight, "Perlin Noise Comparison Tool", NULL, NULL); + if (!window) { + glfwTerminate(); + return false; + } + + glfwMakeContextCurrent(window); + glfwSwapInterval(1); + + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); (void)io; + + ImGui::StyleColorsDark(); + + ImGui_ImplGlfw_InitForOpenGL(window, true); + ImGui_ImplOpenGL3_Init("#version 130"); + + // Initialize with some default views + addComparisonView("FBM", "Grayscale", 0.01, 4, 42, "FBM Grayscale"); + addComparisonView("FBM", "Heatmap", 0.01, 4, 42, "FBM Heatmap"); + addComparisonView("FBM", "Terrain", 0.01, 4, 42, "FBM Terrain"); + + return true; + } + + void initializePresets() { + presets = { + {"Basic Grayscale", 0.01, 1, "Basic", "Grayscale"}, + {"FBM Grayscale", 0.01, 4, "FBM", "Grayscale"}, + {"FBM Terrain", 0.01, 4, "FBM", "Terrain"}, + {"Turbulence", 0.01, 4, "Turbulence", "Grayscale"}, + {"Ridged Multi", 0.01, 4, "Ridged", "Grayscale"}, + {"Large Scale", 0.002, 4, "FBM", "Grayscale"}, + {"Small Scale", 0.05, 4, "FBM", "Grayscale"}, + {"High Octaves", 0.01, 8, "FBM", "Grayscale"} + }; + selectedPreset = 0; + } + + void addComparisonView(const std::string& type, const std::string& cmap, + double sc, int oct, unsigned int sd, const std::string& lbl) { + ComparisonView view; + view.texture = std::make_unique(textureSize, textureSize); + view.scale = sc; + view.octaves = oct; + view.seed = sd; + view.noiseType = type; + view.colorMap = cmap; + view.label = lbl; + + PerlinNoise pn(view.seed); + view.texture->generateNoise(pn, view.scale, view.octaves, view.noiseType, view.colorMap); + + views.push_back(std::move(view)); + } + + void run() { + while (!glfwWindowShouldClose(window)) { + glfwPollEvents(); + + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplGlfw_NewFrame(); + ImGui::NewFrame(); + + renderUI(); + + ImGui::Render(); + + int display_w, display_h; + glfwGetFramebufferSize(window, &display_w, &display_h); + glViewport(0, 0, display_w, display_h); + glClearColor(0.1f, 0.1f, 0.1f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT); + + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + + glfwSwapBuffers(window); + } + } + + void renderUI() { + // Main control panel + ImGui::SetNextWindowPos(ImVec2(0, 0)); + ImGui::SetNextWindowSize(ImVec2(300, windowHeight)); + ImGui::Begin("Controls", nullptr, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove); + + ImGui::Text("Noise Parameters"); + ImGui::Separator(); + + ImGui::SliderDouble("Scale", &scale, 0.001, 0.1, "%.3f"); + ImGui::SliderInt("Octaves", &octaves, 1, 8); + ImGui::InputInt("Seed", (int*)&seed); + + const char* noiseTypes[] = {"Basic", "FBM", "Turbulence", "Ridged"}; + ImGui::Combo("Noise Type", [](void* data, int idx, const char** out_text) { + *out_text = noiseTypes[idx]; + return true; + }, nullptr, IM_ARRAYSIZE(noiseTypes)); + noiseType = noiseTypes[ImGui::GetStateStorage()->GetInt(ImGui::GetID("Noise Type"), 0)]; + + const char* colorMaps[] = {"Grayscale", "Heatmap", "Terrain"}; + ImGui::Combo("Color Map", [](void* data, int idx, const char** out_text) { + *out_text = colorMaps[idx]; + return true; + }, nullptr, IM_ARRAYSIZE(colorMaps)); + colorMap = colorMaps[ImGui::GetStateStorage()->GetInt(ImGui::GetID("Color Map"), 0)]; + + ImGui::Separator(); + ImGui::Text("Texture Size: %d", textureSize); + ImGui::SliderInt("##TexSize", &textureSize, 64, 512); + + ImGui::Separator(); + if (ImGui::Button("Generate Current")) { + std::string label = noiseType + " " + colorMap; + addComparisonView(noiseType, colorMap, scale, octaves, seed, label); + } + + ImGui::SameLine(); + if (ImGui::Button("Clear All")) { + views.clear(); + } + + ImGui::Separator(); + ImGui::Text("Presets"); + + std::vector presetNames; + for (const auto& preset : presets) { + presetNames.push_back(preset.name.c_str()); + } + + ImGui::Combo("##Presets", &selectedPreset, presetNames.data(), presetNames.size()); + + if (ImGui::Button("Add Preset")) { + if (selectedPreset >= 0 && selectedPreset < presets.size()) { + const auto& preset = presets[selectedPreset]; + addComparisonView(preset.noiseType, preset.colorMap, + preset.scale, preset.octaves, seed, preset.name); + } + } + + ImGui::SameLine(); + if (ImGui::Button("Add All Presets")) { + for (const auto& preset : presets) { + addComparisonView(preset.noiseType, preset.colorMap, + preset.scale, preset.octaves, seed, preset.name); + } + } + + ImGui::Separator(); + ImGui::Text("Quick Comparisons"); + + if (ImGui::Button("Scale Comparison")) { + std::vector scales = {0.002, 0.005, 0.01, 0.02, 0.05}; + for (double sc : scales) { + std::string label = "Scale " + std::to_string(sc); + addComparisonView("FBM", "Grayscale", sc, 4, seed, label); + } + } + + ImGui::SameLine(); + if (ImGui::Button("Octave Comparison")) { + for (int oct = 1; oct <= 6; ++oct) { + std::string label = oct + " Octaves"; + addComparisonView("FBM", "Grayscale", 0.01, oct, seed, label); + } + } + + ImGui::End(); + + // Main view area + ImGui::SetNextWindowPos(ImVec2(300, 0)); + ImGui::SetNextWindowSize(ImVec2(windowWidth - 300, windowHeight)); + ImGui::Begin("Noise Comparison", nullptr, + ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | + ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoBringToFrontOnFocus); + + ImVec2 imageSize(textureSize, textureSize); + int itemsPerRow = std::max(1, (int)((windowWidth - 320) / (textureSize + 20))); + + for (size_t i = 0; i < views.size(); ++i) { + auto& view = views[i]; + + if (i % itemsPerRow != 0) ImGui::SameLine(); + + ImGui::BeginGroup(); + view.texture->draw(view.label.c_str(), imageSize); + + // Mini controls for each view + ImGui::PushID(static_cast(i)); + if (ImGui::SmallButton("Regenerate")) { + PerlinNoise pn(view.seed); + view.texture->generateNoise(pn, view.scale, view.octaves, view.noiseType, view.colorMap); + } + ImGui::SameLine(); + if (ImGui::SmallButton("Remove")) { + views.erase(views.begin() + i); + ImGui::PopID(); + break; + } + ImGui::PopID(); + + ImGui::EndGroup(); + } + + ImGui::End(); + } + + ~NoiseComparisonApp() { + ImGui_ImplOpenGL3_Shutdown(); + ImGui_ImplGlfw_Shutdown(); + ImGui::DestroyContext(); + + glfwDestroyWindow(window); + glfwTerminate(); + } +}; + +int main() { + NoiseComparisonApp app; + + if (!app.initialize()) { + std::cerr << "Failed to initialize application!" << std::endl; + return -1; + } + + app.run(); + return 0; +} \ No newline at end of file diff --git a/util/noise/noisetest.cpp b/util/noise/noisetest.cpp new file mode 100644 index 0000000..e13f8c8 --- /dev/null +++ b/util/noise/noisetest.cpp @@ -0,0 +1,306 @@ +// noisetest.cpp +#include "pnoise.hpp" +#include "../bmpwriter.hpp" +#include +#include +#include +#include +#include + +// Convert noise value to grayscale color +Vec3 noiseToColor(double noiseValue) { + float value = static_cast(noiseValue); + return Vec3(value, value, value); +} + +// Convert noise value to color using a blue-to-red colormap +Vec3 noiseToHeatmap(double noiseValue) { + // Blue (0.0) -> Cyan -> Green -> Yellow -> Red (1.0) + float value = static_cast(noiseValue); + + if (value < 0.25f) { + // Blue to Cyan + float t = value / 0.25f; + return Vec3(0.0f, t, 1.0f); + } else if (value < 0.5f) { + // Cyan to Green + float t = (value - 0.25f) / 0.25f; + return Vec3(0.0f, 1.0f, 1.0f - t); + } else if (value < 0.75f) { + // Green to Yellow + float t = (value - 0.5f) / 0.25f; + return Vec3(t, 1.0f, 0.0f); + } else { + // Yellow to Red + float t = (value - 0.75f) / 0.25f; + return Vec3(1.0f, 1.0f - t, 0.0f); + } +} + +// Convert noise value to terrain-like colors +Vec3 noiseToTerrain(double noiseValue) { + float value = static_cast(noiseValue); + + if (value < 0.3f) { + // Deep water to shallow water + return Vec3(0.0f, 0.0f, 0.3f + value * 0.4f); + } else if (value < 0.4f) { + // Sand + return Vec3(0.76f, 0.70f, 0.50f); + } else if (value < 0.6f) { + // Grass + float t = (value - 0.4f) / 0.2f; + return Vec3(0.0f, 0.4f + t * 0.3f, 0.0f); + } else if (value < 0.8f) { + // Forest + return Vec3(0.0f, 0.3f, 0.0f); + } else { + // Mountain to snow + float t = (value - 0.8f) / 0.2f; + return Vec3(0.8f + t * 0.2f, 0.8f + t * 0.2f, 0.8f + t * 0.2f); + } +} + +// Generate basic 2D noise map +void generateBasicNoise(const std::string& filename, int width, int height, + double scale = 0.01, unsigned int seed = 42) { + std::cout << "Generating basic noise: " << filename << std::endl; + + PerlinNoise pn(seed); + std::vector> pixels(height, std::vector(width)); + + for (int y = 0; y < height; ++y) { + for (int x = 0; x < width; ++x) { + double noise = pn.noise(x * scale, y * scale); + pixels[y][x] = noiseToColor(noise); + } + } + + BMPWriter::saveBMP(filename, pixels); +} + +// Generate fractal Brownian motion noise +void generateFBMNoise(const std::string& filename, int width, int height, + size_t octaves, double scale = 0.01, unsigned int seed = 42) { + std::cout << "Generating FBM noise (" << octaves << " octaves): " << filename << std::endl; + + PerlinNoise pn(seed); + std::vector> pixels(height, std::vector(width)); + + for (int y = 0; y < height; ++y) { + for (int x = 0; x < width; ++x) { + double noise = pn.fractal(octaves, x * scale, y * scale); + pixels[y][x] = noiseToColor(noise); + } + } + + BMPWriter::saveBMP(filename, pixels); +} + +// Generate turbulence noise +void generateTurbulenceNoise(const std::string& filename, int width, int height, + size_t octaves, double scale = 0.01, unsigned int seed = 42) { + std::cout << "Generating turbulence noise (" << octaves << " octaves): " << filename << std::endl; + + PerlinNoise pn(seed); + std::vector> pixels(height, std::vector(width)); + + for (int y = 0; y < height; ++y) { + for (int x = 0; x < width; ++x) { + double noise = pn.turbulence(octaves, x * scale, y * scale); + pixels[y][x] = noiseToColor(noise); + } + } + + BMPWriter::saveBMP(filename, pixels); +} + +// Generate ridged multi-fractal noise +void generateRidgedNoise(const std::string& filename, int width, int height, + size_t octaves, double scale = 0.01, unsigned int seed = 42, + double lacunarity = 2.0, double gain = 0.5, double offset = 1.0) { + std::cout << "Generating ridged multi-fractal noise (" << octaves << " octaves): " << filename << std::endl; + + PerlinNoise pn(seed); + std::vector> pixels(height, std::vector(width)); + + for (int y = 0; y < height; ++y) { + for (int x = 0; x < width; ++x) { + double noise = pn.ridgedMultiFractal(octaves, x * scale, y * scale, + 0.0, lacunarity, gain, offset); + pixels[y][x] = noiseToColor(noise); + } + } + + BMPWriter::saveBMP(filename, pixels); +} + +// Generate noise with different color mappings +void generateColoredNoise(const std::string& filename, int width, int height, + double scale = 0.01, unsigned int seed = 42, + const std::string& colorMap = "heatmap") { + std::cout << "Generating colored noise (" << colorMap << "): " << filename << std::endl; + + PerlinNoise pn(seed); + std::vector> pixels(height, std::vector(width)); + + for (int y = 0; y < height; ++y) { + for (int x = 0; x < width; ++x) { + double noise = pn.noise(x * scale, y * scale); + + if (colorMap == "heatmap") { + pixels[y][x] = noiseToHeatmap(noise); + } else if (colorMap == "terrain") { + pixels[y][x] = noiseToTerrain(noise); + } else { + pixels[y][x] = noiseToColor(noise); + } + } + } + + BMPWriter::saveBMP(filename, pixels); +} + +// Generate multi-octave comparison +void generateOctaveComparison(const std::string& baseFilename, int width, int height, + double scale = 0.01, unsigned int seed = 42) { + for (size_t octaves = 1; octaves <= 6; ++octaves) { + std::string filename = baseFilename + "_octaves_" + std::to_string(octaves) + ".bmp"; + generateFBMNoise(filename, width, height, octaves, scale, seed); + } +} + +// Generate scale comparison +void generateScaleComparison(const std::string& baseFilename, int width, int height, + unsigned int seed = 42) { + std::vector scales = {0.002, 0.005, 0.01, 0.02, 0.05, 0.1}; + + for (double scale : scales) { + std::string filename = baseFilename + "_scale_" + std::to_string(scale) + ".bmp"; + generateBasicNoise(filename, width, height, scale, seed); + } +} + +// Generate seed comparison +void generateSeedComparison(const std::string& baseFilename, int width, int height, + double scale = 0.01) { + std::vector seeds = {42, 123, 456, 789, 1000}; + + for (unsigned int seed : seeds) { + std::string filename = baseFilename + "_seed_" + std::to_string(seed) + ".bmp"; + generateBasicNoise(filename, width, height, scale, seed); + } +} + +// Generate combined effects (FBM with different color maps) +void generateCombinedEffects(const std::string& baseFilename, int width, int height, + double scale = 0.01, unsigned int seed = 42) { + PerlinNoise pn(seed); + + // FBM with grayscale + std::vector> pixels1(height, std::vector(width)); + for (int y = 0; y < height; ++y) { + for (int x = 0; x < width; ++x) { + double noise = pn.fractal(4, x * scale, y * scale); + pixels1[y][x] = noiseToColor(noise); + } + } + BMPWriter::saveBMP(baseFilename + "_fbm_grayscale.bmp", pixels1); + + // FBM with heatmap + std::vector> pixels2(height, std::vector(width)); + for (int y = 0; y < height; ++y) { + for (int x = 0; x < width; ++x) { + double noise = pn.fractal(4, x * scale, y * scale); + pixels2[y][x] = noiseToHeatmap(noise); + } + } + BMPWriter::saveBMP(baseFilename + "_fbm_heatmap.bmp", pixels2); + + // FBM with terrain + std::vector> pixels3(height, std::vector(width)); + for (int y = 0; y < height; ++y) { + for (int x = 0; x < width; ++x) { + double noise = pn.fractal(4, x * scale, y * scale); + pixels3[y][x] = noiseToTerrain(noise); + } + } + BMPWriter::saveBMP(baseFilename + "_fbm_terrain.bmp", pixels3); +} + +// Generate 3D slice noise (showing different Z slices) +void generate3DSlices(const std::string& baseFilename, int width, int height, + double scale = 0.01, unsigned int seed = 42) { + PerlinNoise pn(seed); + + std::vector zSlices = {0.0, 0.2, 0.4, 0.6, 0.8, 1.0}; + + for (size_t i = 0; i < zSlices.size(); ++i) { + std::vector> pixels(height, std::vector(width)); + double z = zSlices[i] * 10.0; // Scale Z for meaningful variation + + for (int y = 0; y < height; ++y) { + for (int x = 0; x < width; ++x) { + double noise = pn.noise(x * scale, y * scale, z); + pixels[y][x] = noiseToColor(noise); + } + } + + std::string filename = baseFilename + "_zslice_" + std::to_string(i) + ".bmp"; + BMPWriter::saveBMP(filename, pixels); + std::cout << "Generated 3D slice " << i << ": " << filename << std::endl; + } +} + +int main() { + const int WIDTH = 512; + const int HEIGHT = 512; + + std::cout << "Generating Perlin noise variations..." << std::endl; + std::cout << "=====================================" << std::endl; + + // 1. Basic noise variations + std::cout << "\n1. Basic Noise Variations:" << std::endl; + generateBasicNoise("output/basic_noise.bmp", WIDTH, HEIGHT, 0.01, 42); + + // 2. Fractal Brownian Motion with different octaves + std::cout << "\n2. FBM Noise (Multiple Octaves):" << std::endl; + generateOctaveComparison("output/fbm", WIDTH, HEIGHT, 0.01, 42); + + // 3. Turbulence noise + std::cout << "\n3. Turbulence Noise:" << std::endl; + generateTurbulenceNoise("output/turbulence_4oct.bmp", WIDTH, HEIGHT, 4, 0.01, 42); + generateTurbulenceNoise("output/turbulence_6oct.bmp", WIDTH, HEIGHT, 6, 0.01, 42); + + // 4. Ridged multi-fractal noise + std::cout << "\n4. Ridged Multi-Fractal Noise:" << std::endl; + generateRidgedNoise("output/ridged_4oct.bmp", WIDTH, HEIGHT, 4, 0.01, 42, 2.0, 0.5, 1.0); + generateRidgedNoise("output/ridged_6oct.bmp", WIDTH, HEIGHT, 6, 0.01, 42, 2.0, 0.5, 1.0); + + // 5. Different color mappings + std::cout << "\n5. Color Mappings:" << std::endl; + generateColoredNoise("output/heatmap_noise.bmp", WIDTH, HEIGHT, 0.01, 42, "heatmap"); + generateColoredNoise("output/terrain_noise.bmp", WIDTH, HEIGHT, 0.01, 42, "terrain"); + + // 6. Scale variations + std::cout << "\n6. Scale Variations:" << std::endl; + generateScaleComparison("output/scale_test", WIDTH, HEIGHT, 42); + + // 7. Seed variations + std::cout << "\n7. Seed Variations:" << std::endl; + generateSeedComparison("output/seed_test", WIDTH, HEIGHT, 0.01); + + // 8. Combined effects + std::cout << "\n8. Combined Effects:" << std::endl; + generateCombinedEffects("output/combined", WIDTH, HEIGHT, 0.01, 42); + + // 9. 3D slices + std::cout << "\n9. 3D Slices:" << std::endl; + generate3DSlices("output/3d_slice", WIDTH, HEIGHT, 0.01, 42); + + std::cout << "\n=====================================" << std::endl; + std::cout << "All noise maps generated successfully!" << std::endl; + std::cout << "Check the 'output' directory for BMP files." << std::endl; + + return 0; +} \ No newline at end of file