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