This commit is contained in:
yggdrasil75
2025-12-26 12:50:24 -05:00
parent 7252c655a2
commit ce27939e4e
2 changed files with 607 additions and 0 deletions

322
latlonv.cpp Normal file
View File

@@ -0,0 +1,322 @@
#include <imgui.h>
#include <imgui_impl_glfw.h>
#include <imgui_impl_opengl3.h>
#include <GLFW/glfw3.h>
#include <vector>
#include <cmath>
#include <random>
#include <algorithm>
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<Voxel> voxels;
PlanetConfig config;
std::vector<float> 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<unsigned char> 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;
}

285
tests/g3test.cpp Normal file
View File

@@ -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 <cmath>
#include <random>
#include <filesystem>
// Separate component storage
class ComponentManager {
private:
std::unordered_map<size_t, Vec4ui8> colors_;
std::unordered_map<size_t, float> 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<Vec3f> generate_noise_positions(int width, int height, float scale = 0.01f, float threshold = 0.3f) {
std::vector<Vec3f> 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<float>(x) / width;
float ny = static_cast<float>(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<Vec3f> generate_3d_noise_positions(int grid_size, float scale = 0.02f, float threshold = 0.4f) {
std::vector<Vec3f> 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<float>(x) / sizehalf;
float ny = static_cast<float>(y) / sizehalf;
float nz = static_cast<float>(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<uint8_t>(normalized.x * 255);
uint8_t g = static_cast<uint8_t>(normalized.y * 255);
uint8_t b = static_cast<uint8_t>(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<uint8_t>(noise_r * 255);
uint8_t g = static_cast<uint8_t>(noise_g * 255);
uint8_t b = static_cast<uint8_t>(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<size_t> 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<ViewAngle> 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<float> 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;
}