From 7252c655a2ee7dd9cb3a74b072ccbfde4d5f2d88 Mon Sep 17 00:00:00 2001 From: yggdrasil75 Date: Fri, 26 Dec 2025 12:50:19 -0500 Subject: [PATCH 1/3] meh --- util/output/bmpwriter.hpp | 2 +- util/output/frame.hpp | 4 ++-- util/vectorlogic/vec3.hpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/util/output/bmpwriter.hpp b/util/output/bmpwriter.hpp index f1db6e6..1173661 100644 --- a/util/output/bmpwriter.hpp +++ b/util/output/bmpwriter.hpp @@ -143,7 +143,7 @@ public: return true; } - static bool saveBMP(const std::string& filename, frame& frame) { + static bool saveBMP(const std::string& filename, const frame& frame) { if (frame.colorFormat == frame::colormap::RGB) { return saveBMP(filename, frame.getData(), frame.getWidth(), frame.getHeight()); } else if (frame.colorFormat == frame::colormap::RGBA) { diff --git a/util/output/frame.hpp b/util/output/frame.hpp index 9b29461..28fa3ad 100644 --- a/util/output/frame.hpp +++ b/util/output/frame.hpp @@ -48,11 +48,11 @@ public: colormap colorFormat = colormap::RGB; compresstype cformat = compresstype::RAW; - const size_t& getWidth() { + const size_t& getWidth() const { return width; } - const size_t& getHeight() { + const size_t& getHeight() const { return height; } frame() {}; diff --git a/util/vectorlogic/vec3.hpp b/util/vectorlogic/vec3.hpp index 5902f5f..56c98cd 100644 --- a/util/vectorlogic/vec3.hpp +++ b/util/vectorlogic/vec3.hpp @@ -14,7 +14,7 @@ public: Vec3() : x(0), y(0), z(0) {} Vec3(T x, T y, T z) : x(x), y(y), z(z) {} Vec3(T scalar) : x(scalar), y(scalar), z(scalar) {} - Vec3(float[3] acd) : x(acd[0]), y(acd[1]), z(acd[2]) {} + Vec3(float acd[3]) : x(acd[0]), y(acd[1]), z(acd[2]) {} Vec3(const class Vec2& vec2, T z = 0); From ce27939e4e2ae3e59bdf89050f732fd9e1623f15 Mon Sep 17 00:00:00 2001 From: yggdrasil75 Date: Fri, 26 Dec 2025 12:50:24 -0500 Subject: [PATCH 2/3] meh2 --- latlonv.cpp | 322 +++++++++++++++++++++++++++++++++++++++++++++++ tests/g3test.cpp | 285 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 607 insertions(+) create mode 100644 latlonv.cpp create mode 100644 tests/g3test.cpp diff --git a/latlonv.cpp b/latlonv.cpp new file mode 100644 index 0000000..fb5b8d6 --- /dev/null +++ b/latlonv.cpp @@ -0,0 +1,322 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +struct Voxel { + float density; + unsigned char material; // 0: air, 1: rock, 2: dirt, 3: grass +}; + +struct PlanetConfig { + int resolution = 64; + float radius = 10.0f; + float noiseScale = 0.1f; + float amplitude = 1.0f; + int seed = 42; +}; + +class SphericalVoxelPlanet { +private: + std::vector voxels; + PlanetConfig config; + std::vector distanceField; + GLuint textureID; + +public: + SphericalVoxelPlanet() : textureID(0) { + generate(); + } + + ~SphericalVoxelPlanet() { + if (textureID) { + glDeleteTextures(1, &textureID); + } + } + + // Convert spherical to Cartesian coordinates + static glm::vec3 sphericalToCartesian(float lat, float lon, float dist) { + float latRad = glm::radians(lat); + float lonRad = glm::radians(lon); + return glm::vec3( + dist * cos(latRad) * cos(lonRad), + dist * cos(latRad) * sin(lonRad), + dist * sin(latRad) + ); + } + + // 3D noise function for spherical coordinates + float sphericalNoise(float lat, float lon, float r, int seed) { + glm::vec3 pos = sphericalToCartesian(lat, lon, 1.0f); + pos *= config.noiseScale; + pos.x += seed; + + // Simple noise function + float fx = pos.x * 12.9898f + pos.y * 78.233f + pos.z * 137.631f; + return glm::fract(sin(fx) * 43758.5453f); + } + + void generate() { + int res = config.resolution; + voxels.resize(res * res * res); + distanceField.resize(res * res * res); + + float latStep = 180.0f / res; + float lonStep = 360.0f / res; + float distStep = config.radius * 2.0f / res; + + for (int i = 0; i < res; i++) { // latitude + float lat = -90.0f + i * latStep; + float latRad = glm::radians(lat); + + for (int j = 0; j < res; j++) { // longitude + float lon = j * lonStep; + + for (int k = 0; k < res; k++) { // distance from center + float dist = k * distStep; + float normalizedDist = dist / (config.radius * 2.0f); + + int idx = i * res * res + j * res + k; + + // Base density (sphere) + float baseDensity = config.radius - dist; + + // Add noise based on latitude and longitude + float noise = sphericalNoise(lat, lon, dist, config.seed); + float altitude = 1.0f - std::abs(lat / 90.0f); // Poles are higher + + float finalDensity = baseDensity + noise * config.amplitude * altitude; + + distanceField[idx] = finalDensity; + + // Assign material based on density and position + if (finalDensity > 0.5f) { + if (k > res * 0.9f) { // Surface + voxels[idx].material = 3; // Grass + } else if (k > res * 0.7f) { + voxels[idx].material = 2; // Dirt + } else { + voxels[idx].material = 1; // Rock + } + voxels[idx].density = 1.0f; + } else { + voxels[idx].material = 0; // Air + voxels[idx].density = 0.0f; + } + } + } + } + + updateTexture(); + } + + void updateTexture() { + int res = config.resolution; + std::vector textureData(res * res * res * 3); + + for (int i = 0; i < res; i++) { + for (int j = 0; j < res; j++) { + for (int k = 0; k < res; k++) { + int idx = i * res * res + j * res + k; + int texIdx = (i * res * res + j * res + k) * 3; + + switch (voxels[idx].material) { + case 0: // Air + textureData[texIdx] = 0; + textureData[texIdx + 1] = 0; + textureData[texIdx + 2] = 0; + break; + case 1: // Rock + textureData[texIdx] = 100; + textureData[texIdx + 1] = 100; + textureData[texIdx + 2] = 100; + break; + case 2: // Dirt + textureData[texIdx] = 101; + textureData[texIdx + 1] = 67; + textureData[texIdx + 2] = 33; + break; + case 3: // Grass + textureData[texIdx] = 34; + textureData[texIdx + 1] = 139; + textureData[texIdx + 2] = 34; + break; + } + } + } + } + + if (textureID == 0) { + glGenTextures(1, &textureID); + } + + glBindTexture(GL_TEXTURE_3D, textureID); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB, res, res, res, 0, GL_RGB, GL_UNSIGNED_BYTE, textureData.data()); + } + + void renderUI() { + ImGui::Begin("Planet Generator"); + + if (ImGui::SliderInt("Resolution", &config.resolution, 32, 128)) { + generate(); + } + + if (ImGui::SliderFloat("Radius", &config.radius, 5.0f, 20.0f)) { + generate(); + } + + if (ImGui::SliderFloat("Noise Scale", &config.noiseScale, 0.01f, 0.5f)) { + generate(); + } + + if (ImGui::SliderFloat("Amplitude", &config.amplitude, 0.0f, 3.0f)) { + generate(); + } + + if (ImGui::SliderInt("Seed", &config.seed, 0, 1000)) { + generate(); + } + + if (ImGui::Button("Randomize Seed")) { + config.seed = rand() % 1000; + generate(); + } + + ImGui::End(); + } + + void renderPlanet() { + // Simple raycasting visualization using distance field + // This is a basic implementation - for better results, implement proper raycasting + ImDrawList* drawList = ImGui::GetBackgroundDrawList(); + ImVec2 center = ImGui::GetIO().DisplaySize * 0.5f; + float displaySize = std::min(ImGui::GetIO().DisplaySize.x, ImGui::GetIO().DisplaySize.y) * 0.4f; + + int slices = 32; + float angleStep = 2.0f * 3.14159f / slices; + + // Draw planet outline + for (int i = 0; i < slices; i++) { + float angle1 = i * angleStep; + float angle2 = (i + 1) % slices * angleStep; + + ImVec2 p1 = center + ImVec2(cos(angle1) * displaySize, sin(angle1) * displaySize); + ImVec2 p2 = center + ImVec2(cos(angle2) * displaySize, sin(angle2) * displaySize); + + drawList->AddLine(p1, p2, IM_COL32(255, 255, 255, 255)); + } + + // Draw some sample voxels + int res = config.resolution; + for (int i = 0; i < 8; i++) { + for (int j = 0; j < 8; j++) { + float lat = -90.0f + 180.0f * (i / 7.0f); + float lon = 360.0f * (j / 7.0f); + + // Find surface voxel + for (int k = res - 1; k >= 0; k--) { + int idx = (i * res / 8) * res * res + (j * res / 8) * res + k; + if (voxels[idx].material != 0) { + // Convert spherical to screen coordinates + float latRad = glm::radians(lat); + float lonRad = glm::radians(lon); + float dist = (k / (float)res) * config.radius * 2.0f; + + float screenDist = (dist / (config.radius * 2.0f)) * displaySize; + ImVec2 pos = center + ImVec2( + cos(latRad) * cos(lonRad) * screenDist, + sin(latRad) * screenDist + ); + + // Color based on material + ImU32 color; + switch (voxels[idx].material) { + case 1: color = IM_COL32(100, 100, 100, 255); break; + case 2: color = IM_COL32(101, 67, 33, 255); break; + case 3: color = IM_COL32(34, 139, 34, 255); break; + default: color = IM_COL32(0, 0, 0, 0); + } + + if (color != 0) { + drawList->AddCircleFilled(pos, 2.0f, color); + } + break; + } + } + } + } + } +}; + +int main() { + // GLFW initialization + if (!glfwInit()) return -1; + + GLFWwindow* window = glfwCreateWindow(1280, 720, "Voxel Planet Generator", NULL, NULL); + if (!window) { + glfwTerminate(); + return -1; + } + + glfwMakeContextCurrent(window); + glfwSwapInterval(1); + + // ImGui initialization + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImGuiIO& io = ImGui::GetIO(); + + ImGui::StyleColorsDark(); + ImGui_ImplGlfw_InitForOpenGL(window, true); + ImGui_ImplOpenGL3_Init("#version 130"); + + SphericalVoxelPlanet planet; + + // Main loop + while (!glfwWindowShouldClose(window)) { + glfwPollEvents(); + + // Start ImGui frame + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplGlfw_NewFrame(); + ImGui::NewFrame(); + + // Render UI + planet.renderUI(); + + // Clear screen + 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); + + // Render planet + planet.renderPlanet(); + + // Render ImGui + ImGui::Render(); + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + + glfwSwapBuffers(window); + } + + // Cleanup + ImGui_ImplOpenGL3_Shutdown(); + ImGui_ImplGlfw_Shutdown(); + ImGui::DestroyContext(); + + glfwDestroyWindow(window); + glfwTerminate(); + + return 0; +} diff --git a/tests/g3test.cpp b/tests/g3test.cpp new file mode 100644 index 0000000..e3e0a49 --- /dev/null +++ b/tests/g3test.cpp @@ -0,0 +1,285 @@ +#include "../util/grid/grid3.hpp" +#include "../util/output/bmpwriter.hpp" +#include "../util/noise/pnoise2.hpp" +#include "../util/timing_decorator.cpp" +#include +#include +#include + +// Separate component storage +class ComponentManager { +private: + std::unordered_map colors_; + std::unordered_map sizes_; + +public: + void set_color(size_t id, const Vec4ui8& color) { colors_[id] = color; } + Vec4ui8 get_color(size_t id) const { + auto it = colors_.find(id); + return it != colors_.end() ? it->second : Vec4ui8(1, 1, 1, 1); + } +}; + +// Generate noise-based positions +std::vector generate_noise_positions(int width, int height, float scale = 0.01f, float threshold = 0.3f) { + std::vector positions; + PNoise2 noise_generator(std::random_device{}()); + + // Generate positions based on 2D noise + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + // Convert to normalized coordinates + float nx = static_cast(x) / width; + float ny = static_cast(y) / height; + + // Generate noise at multiple octaves for more interesting patterns + float noise_value = 0.0f; + float amplitude = 1.0f; + float frequency = 1.0f; + float max_value = 0.0f; + + for (int i = 0; i < 4; i++) { + Vec2 sample_point(nx * frequency * scale, ny * frequency * scale); + noise_value += amplitude * noise_generator.permute(sample_point); + max_value += amplitude; + amplitude *= 0.5f; + frequency *= 2.0f; + } + + noise_value = (noise_value / max_value + 1.0f) * 0.5f; // Normalize to [0, 1] + + // Threshold to create sparse distribution + if (noise_value > threshold) { + // Map to world coordinates [-512, 512] + float world_x = (nx * 2.0f - 1.0f) * 512.0f; + float world_y = (ny * 2.0f - 1.0f) * 512.0f; + float world_z = noise_value * 100.0f; // Use noise value for height + + positions.emplace_back(world_x, world_y, world_z); + } + } + } + + return positions; +} + +// Generate 3D noise-based positions (more varied in 3D space) +std::vector generate_3d_noise_positions(int grid_size, float scale = 0.02f, float threshold = 0.4f) { + std::vector positions; + PNoise2 noise_generator(std::random_device{}()); + int sizehalf = grid_size / 2; + for (int x = 0; x < grid_size; x++) { + for (int y = 0; y < grid_size; y++) { + for (int z = 0; z < grid_size; z++) { + // Convert to normalized coordinates + float nx = static_cast(x) / sizehalf; + float ny = static_cast(y) / sizehalf; + float nz = static_cast(z) / sizehalf; + + // Generate 3D noise + Vec3f sample_point(nx, ny, nz); + float noise_value = noise_generator.permute(sample_point); + + // Threshold to create sparse distribution + if (noise_value > threshold) { + // Map to world coordinates [-512, 512] + float world_x = (x - sizehalf); + float world_y = (y - sizehalf); + float world_z = (z - sizehalf); + + positions.emplace_back(world_x, world_y, world_z); + } + } + } + } + + return positions; +} + +// Generate gradient color based on position +Vec4ui8 get_gradient_color(const Vec3f& pos, const Vec3f& min_pos, const Vec3f& max_pos) { + // Normalize position to [0, 1] + Vec3f normalized( + (pos.x - min_pos.x) / (max_pos.x - min_pos.x), + (pos.y - min_pos.y) / (max_pos.y - min_pos.y), + (pos.z - min_pos.z) / (max_pos.z - min_pos.z) + ); + + // Create RGB gradient based on normalized coordinates + uint8_t r = static_cast(normalized.x * 255); + uint8_t g = static_cast(normalized.y * 255); + uint8_t b = static_cast(normalized.z * 255); + + return Vec4ui8(r, g, b, 255); +} + +// Generate noise-based color +Vec4ui8 get_noise_color(const Vec3f& pos, PNoise2& noise_generator) { + // Use noise to generate color + Vec3f sample_point(pos.x * 0.005f, pos.y * 0.005f, pos.z * 0.005f); + float noise_r = noise_generator.permute(sample_point); + noise_r = (noise_r + 1.0f) * 0.5f; // Normalize to [0, 1] + + sample_point = Vec3f(pos.x * 0.007f, pos.y * 0.007f, pos.z * 0.007f); + float noise_g = noise_generator.permute(sample_point); + noise_g = (noise_g + 1.0f) * 0.5f; + + sample_point = Vec3f(pos.x * 0.009f, pos.y * 0.009f, pos.z * 0.009f); + float noise_b = noise_generator.permute(sample_point); + noise_b = (noise_b + 1.0f) * 0.5f; + + uint8_t r = static_cast(noise_r * 255); + uint8_t g = static_cast(noise_g * 255); + uint8_t b = static_cast(noise_b * 255); + + return Vec4ui8(r, g, b, 255); +} + +// Function to save frame with given filename +bool save_frame(const frame& f, const std::string& filename) { + return BMPWriter::saveBMP(filename, f); +} + +// Create directory if it doesn't exist +void ensure_directory(const std::string& path) { + std::filesystem::create_directories(path); +} + +int main() { + // Create output directory + ensure_directory("output"); + + // Create pure spatial grid + Grid3 grid(10.0f, 15.0f); // Larger cell size for sparse distribution + + // Separate component storage + ComponentManager components; + + // Generate noise-based positions + std::cout << "Generating noise-based positions..." << std::endl; + + // Option 1: 2D noise (faster) + // auto positions = generate_noise_positions(200, 200, 0.02f, 0.4f); + + // Option 2: 3D noise (more interesting, but slower) + auto positions = generate_3d_noise_positions(1024, 0.05f, 0.5f); + + std::cout << "Generated " << positions.size() << " positions" << std::endl; + + // Add positions to grid + std::cout << "Adding positions to grid..." << std::endl; + std::vector ids = grid.bulk_add_positions(positions); + + // Generate bounds for color gradient + auto bounds = grid.get_bounds(); + Vec3f min_pos = bounds.first; + Vec3f max_pos = bounds.second; + + // Create noise generator for colors + PNoise2 color_noise_generator(42); // Fixed seed for consistent colors + + // Assign colors to components + std::cout << "Assigning colors..." << std::endl; + for (size_t i = 0; i < ids.size(); i++) { + Vec3f pos = grid.get_position(ids[i]); + + // Choose color method: + // Vec4ui8 color = get_gradient_color(pos, min_pos, max_pos); + Vec4ui8 color = get_noise_color(pos, color_noise_generator); + + components.set_color(ids[i], color); + } + + // Query neighbors for first few points (optional) + if (!ids.empty()) { + auto neighbors = grid.get_neighbors(ids[0]); + std::cout << "First point has " << neighbors.size() << " neighbors" << std::endl; + } + + // Region to render (full noise map area) + Vec3f render_min(-512, -512, -512); + Vec3f render_max(512, 512, 512); + Vec2 resolution(1024, 1024); // Higher resolution for better detail + + // Define multiple view angles + struct ViewAngle { + std::string name; + Vec3f eye_position; + Vec3f look_at; + }; + + std::vector view_angles = { + // Orthographic views + {"front", Vec3f(0, 0, -800), Vec3f(0, 0, 0)}, + {"back", Vec3f(0, 0, 800), Vec3f(0, 0, 0)}, + {"left", Vec3f(-800, 0, 0), Vec3f(0, 0, 0)}, + {"right", Vec3f(800, 0, 0), Vec3f(0, 0, 0)}, + {"top", Vec3f(0, 800, 0), Vec3f(0, 0, 0)}, + {"bottom", Vec3f(0, -800, 0), Vec3f(0, 0, 0)}, + + // 45° angle views + {"front_right_top", Vec3f(800, 800, -800), Vec3f(0, 0, 0)}, + {"front_left_top", Vec3f(-800, 800, -800), Vec3f(0, 0, 0)}, + {"front_right_bottom", Vec3f(800, -800, -800), Vec3f(0, 0, 0)}, + {"front_left_bottom", Vec3f(-800, -800, -800), Vec3f(0, 0, 0)}, + + {"back_right_top", Vec3f(800, 800, 800), Vec3f(0, 0, 0)}, + {"back_left_top", Vec3f(-800, 800, 800), Vec3f(0, 0, 0)}, + {"back_right_bottom", Vec3f(800, -800, 800), Vec3f(0, 0, 0)}, + {"back_left_bottom", Vec3f(-800, -800, 800), Vec3f(0, 0, 0)}, + + // Additional interesting angles + {"diagonal_xy", Vec3f(800, 800, 0), Vec3f(0, 0, 0)}, + {"diagonal_xz", Vec3f(800, 0, 800), Vec3f(0, 0, 0)}, + {"diagonal_yz", Vec3f(0, 800, 800), Vec3f(0, 0, 0)}, + + // Isometric-like views + {"isometric_1", Vec3f(600, 600, 600), Vec3f(0, 0, 0)}, + {"isometric_2", Vec3f(-600, 600, 600), Vec3f(0, 0, 0)}, + {"isometric_3", Vec3f(600, -600, 600), Vec3f(0, 0, 0)}, + {"isometric_4", Vec3f(-600, -600, 600), Vec3f(0, 0, 0)} + }; + + // Render and save from each angle + std::cout << "Rendering from multiple angles..." << std::endl; + int saved_count = 0; + + for (const auto& view : view_angles) { + // Create view ray + Vec3f direction = (view.look_at - view.eye_position).normalized(); + Ray3 view_ray(view.eye_position, direction); + + // Render the frame + auto frame = Grid3View::render_region( + grid, + render_min, + render_max, + resolution, + view_ray, + [&](size_t id) { return components.get_color(id); }, + frame::colormap::RGBA, + Vec4ui8(0, 0, 0, 255) // Black background + ); + + // Save the rendered frame + std::string filename = "output/view_" + view.name + ".bmp"; + bool saved = save_frame(frame, filename); + + if (saved) { + saved_count++; + std::cout << "Saved: " << filename << std::endl; + } else { + std::cout << "Failed to save: " << filename << std::endl; + } + } + + std::cout << "\nSuccessfully saved " << saved_count << " out of " << view_angles.size() + << " views to output/" << std::endl; + + // Optional: Save a summary image with grid statistics + std::cout << "\nGrid statistics:" << std::endl; + std::cout << "Total positions: " << grid.size() << std::endl; + std::cout << "Bounds: [" << min_pos << "] to [" << max_pos << "]" << std::endl; + + return 0; +} \ No newline at end of file From cd60918540a8aed49f2f99be7af68728db03402d Mon Sep 17 00:00:00 2001 From: yggdrasil75 Date: Fri, 26 Dec 2025 12:58:30 -0500 Subject: [PATCH 3/3] woops --- glm | 1 + latlonv.cpp | 322 ---------------------------------------------------- 2 files changed, 1 insertion(+), 322 deletions(-) create mode 160000 glm delete mode 100644 latlonv.cpp diff --git a/glm b/glm new file mode 160000 index 0000000..8f6213d --- /dev/null +++ b/glm @@ -0,0 +1 @@ +Subproject commit 8f6213d379a904f5ae910e09a114e066e25faf57 diff --git a/latlonv.cpp b/latlonv.cpp deleted file mode 100644 index fb5b8d6..0000000 --- a/latlonv.cpp +++ /dev/null @@ -1,322 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -struct Voxel { - float density; - unsigned char material; // 0: air, 1: rock, 2: dirt, 3: grass -}; - -struct PlanetConfig { - int resolution = 64; - float radius = 10.0f; - float noiseScale = 0.1f; - float amplitude = 1.0f; - int seed = 42; -}; - -class SphericalVoxelPlanet { -private: - std::vector voxels; - PlanetConfig config; - std::vector distanceField; - GLuint textureID; - -public: - SphericalVoxelPlanet() : textureID(0) { - generate(); - } - - ~SphericalVoxelPlanet() { - if (textureID) { - glDeleteTextures(1, &textureID); - } - } - - // Convert spherical to Cartesian coordinates - static glm::vec3 sphericalToCartesian(float lat, float lon, float dist) { - float latRad = glm::radians(lat); - float lonRad = glm::radians(lon); - return glm::vec3( - dist * cos(latRad) * cos(lonRad), - dist * cos(latRad) * sin(lonRad), - dist * sin(latRad) - ); - } - - // 3D noise function for spherical coordinates - float sphericalNoise(float lat, float lon, float r, int seed) { - glm::vec3 pos = sphericalToCartesian(lat, lon, 1.0f); - pos *= config.noiseScale; - pos.x += seed; - - // Simple noise function - float fx = pos.x * 12.9898f + pos.y * 78.233f + pos.z * 137.631f; - return glm::fract(sin(fx) * 43758.5453f); - } - - void generate() { - int res = config.resolution; - voxels.resize(res * res * res); - distanceField.resize(res * res * res); - - float latStep = 180.0f / res; - float lonStep = 360.0f / res; - float distStep = config.radius * 2.0f / res; - - for (int i = 0; i < res; i++) { // latitude - float lat = -90.0f + i * latStep; - float latRad = glm::radians(lat); - - for (int j = 0; j < res; j++) { // longitude - float lon = j * lonStep; - - for (int k = 0; k < res; k++) { // distance from center - float dist = k * distStep; - float normalizedDist = dist / (config.radius * 2.0f); - - int idx = i * res * res + j * res + k; - - // Base density (sphere) - float baseDensity = config.radius - dist; - - // Add noise based on latitude and longitude - float noise = sphericalNoise(lat, lon, dist, config.seed); - float altitude = 1.0f - std::abs(lat / 90.0f); // Poles are higher - - float finalDensity = baseDensity + noise * config.amplitude * altitude; - - distanceField[idx] = finalDensity; - - // Assign material based on density and position - if (finalDensity > 0.5f) { - if (k > res * 0.9f) { // Surface - voxels[idx].material = 3; // Grass - } else if (k > res * 0.7f) { - voxels[idx].material = 2; // Dirt - } else { - voxels[idx].material = 1; // Rock - } - voxels[idx].density = 1.0f; - } else { - voxels[idx].material = 0; // Air - voxels[idx].density = 0.0f; - } - } - } - } - - updateTexture(); - } - - void updateTexture() { - int res = config.resolution; - std::vector textureData(res * res * res * 3); - - for (int i = 0; i < res; i++) { - for (int j = 0; j < res; j++) { - for (int k = 0; k < res; k++) { - int idx = i * res * res + j * res + k; - int texIdx = (i * res * res + j * res + k) * 3; - - switch (voxels[idx].material) { - case 0: // Air - textureData[texIdx] = 0; - textureData[texIdx + 1] = 0; - textureData[texIdx + 2] = 0; - break; - case 1: // Rock - textureData[texIdx] = 100; - textureData[texIdx + 1] = 100; - textureData[texIdx + 2] = 100; - break; - case 2: // Dirt - textureData[texIdx] = 101; - textureData[texIdx + 1] = 67; - textureData[texIdx + 2] = 33; - break; - case 3: // Grass - textureData[texIdx] = 34; - textureData[texIdx + 1] = 139; - textureData[texIdx + 2] = 34; - break; - } - } - } - } - - if (textureID == 0) { - glGenTextures(1, &textureID); - } - - glBindTexture(GL_TEXTURE_3D, textureID); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); - glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB, res, res, res, 0, GL_RGB, GL_UNSIGNED_BYTE, textureData.data()); - } - - void renderUI() { - ImGui::Begin("Planet Generator"); - - if (ImGui::SliderInt("Resolution", &config.resolution, 32, 128)) { - generate(); - } - - if (ImGui::SliderFloat("Radius", &config.radius, 5.0f, 20.0f)) { - generate(); - } - - if (ImGui::SliderFloat("Noise Scale", &config.noiseScale, 0.01f, 0.5f)) { - generate(); - } - - if (ImGui::SliderFloat("Amplitude", &config.amplitude, 0.0f, 3.0f)) { - generate(); - } - - if (ImGui::SliderInt("Seed", &config.seed, 0, 1000)) { - generate(); - } - - if (ImGui::Button("Randomize Seed")) { - config.seed = rand() % 1000; - generate(); - } - - ImGui::End(); - } - - void renderPlanet() { - // Simple raycasting visualization using distance field - // This is a basic implementation - for better results, implement proper raycasting - ImDrawList* drawList = ImGui::GetBackgroundDrawList(); - ImVec2 center = ImGui::GetIO().DisplaySize * 0.5f; - float displaySize = std::min(ImGui::GetIO().DisplaySize.x, ImGui::GetIO().DisplaySize.y) * 0.4f; - - int slices = 32; - float angleStep = 2.0f * 3.14159f / slices; - - // Draw planet outline - for (int i = 0; i < slices; i++) { - float angle1 = i * angleStep; - float angle2 = (i + 1) % slices * angleStep; - - ImVec2 p1 = center + ImVec2(cos(angle1) * displaySize, sin(angle1) * displaySize); - ImVec2 p2 = center + ImVec2(cos(angle2) * displaySize, sin(angle2) * displaySize); - - drawList->AddLine(p1, p2, IM_COL32(255, 255, 255, 255)); - } - - // Draw some sample voxels - int res = config.resolution; - for (int i = 0; i < 8; i++) { - for (int j = 0; j < 8; j++) { - float lat = -90.0f + 180.0f * (i / 7.0f); - float lon = 360.0f * (j / 7.0f); - - // Find surface voxel - for (int k = res - 1; k >= 0; k--) { - int idx = (i * res / 8) * res * res + (j * res / 8) * res + k; - if (voxels[idx].material != 0) { - // Convert spherical to screen coordinates - float latRad = glm::radians(lat); - float lonRad = glm::radians(lon); - float dist = (k / (float)res) * config.radius * 2.0f; - - float screenDist = (dist / (config.radius * 2.0f)) * displaySize; - ImVec2 pos = center + ImVec2( - cos(latRad) * cos(lonRad) * screenDist, - sin(latRad) * screenDist - ); - - // Color based on material - ImU32 color; - switch (voxels[idx].material) { - case 1: color = IM_COL32(100, 100, 100, 255); break; - case 2: color = IM_COL32(101, 67, 33, 255); break; - case 3: color = IM_COL32(34, 139, 34, 255); break; - default: color = IM_COL32(0, 0, 0, 0); - } - - if (color != 0) { - drawList->AddCircleFilled(pos, 2.0f, color); - } - break; - } - } - } - } - } -}; - -int main() { - // GLFW initialization - if (!glfwInit()) return -1; - - GLFWwindow* window = glfwCreateWindow(1280, 720, "Voxel Planet Generator", NULL, NULL); - if (!window) { - glfwTerminate(); - return -1; - } - - glfwMakeContextCurrent(window); - glfwSwapInterval(1); - - // ImGui initialization - IMGUI_CHECKVERSION(); - ImGui::CreateContext(); - ImGuiIO& io = ImGui::GetIO(); - - ImGui::StyleColorsDark(); - ImGui_ImplGlfw_InitForOpenGL(window, true); - ImGui_ImplOpenGL3_Init("#version 130"); - - SphericalVoxelPlanet planet; - - // Main loop - while (!glfwWindowShouldClose(window)) { - glfwPollEvents(); - - // Start ImGui frame - ImGui_ImplOpenGL3_NewFrame(); - ImGui_ImplGlfw_NewFrame(); - ImGui::NewFrame(); - - // Render UI - planet.renderUI(); - - // Clear screen - 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); - - // Render planet - planet.renderPlanet(); - - // Render ImGui - ImGui::Render(); - ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); - - glfwSwapBuffers(window); - } - - // Cleanup - ImGui_ImplOpenGL3_Shutdown(); - ImGui_ImplGlfw_Shutdown(); - ImGui::DestroyContext(); - - glfwDestroyWindow(window); - glfwTerminate(); - - return 0; -}