#include #include #include #include #include #include #include #include #include #include #include #include #include #include "util/timing_decorator.hpp" #include "util/vec3.hpp" #include "util/vec4.hpp" #include "util/bmpwriter.hpp" #include "util/voxelgrid.hpp" std::vector generateSphere(int numPoints, float radius = 1.0f, float wiggleAmount = 0.1f) { printf("Generating sphere with %d points using grid method...\n", numPoints); printf("Wiggle amount: %.3f\n", wiggleAmount); std::vector points; // Random number generator for wiggling std::random_device rd; std::mt19937 gen(rd()); std::uniform_real_distribution dist(-1.0f, 1.0f); // Calculate grid resolution based on desired number of points // For a sphere, we need to account that only about 52% of points in a cube will be inside the sphere int gridRes = static_cast(std::cbrt(numPoints / 0.52f)) + 1; printf("Using grid resolution: %d x %d x %d\n", gridRes, gridRes, gridRes); // Calculate voxel size for wiggling (based on average distance between points) float voxelSize = 2.0f / gridRes; float maxWiggle = wiggleAmount * voxelSize; printf("Voxel size: %.4f, Max wiggle: %.4f\n", voxelSize, maxWiggle); // Generate points in a cube from -1 to 1 in all dimensions for (int x = 0; x < gridRes; ++x) { for (int y = 0; y < gridRes; ++y) { for (int z = 0; z < gridRes; ++z) { // Convert grid coordinates to normalized cube coordinates [-1, 1] Vec3 point( (2.0f * x / (gridRes - 1)) - 1.0f, (2.0f * y / (gridRes - 1)) - 1.0f, (2.0f * z / (gridRes - 1)) - 1.0f ); // Check if point is inside the unit sphere if (point.lengthSquared() <= 1.0f) { // Apply randomized wiggling Vec3 wiggle( dist(gen) * maxWiggle, dist(gen) * maxWiggle, dist(gen) * maxWiggle ); Vec3 wiggledPoint = point + wiggle; // Re-normalize to maintain spherical shape while preserving the wiggle // This ensures the point stays within the sphere while having natural variation float currentLength = wiggledPoint.length(); if (currentLength > 1.0f) { // Scale back to unit sphere surface, but preserve the wiggle direction wiggledPoint = wiggledPoint * (1.0f / currentLength); } points.push_back(wiggledPoint * radius); // Scale by radius } } } } printf("Generated %zu points inside sphere\n", points.size()); // If we have too many points, randomly sample down to the desired number if (points.size() > static_cast(numPoints)) { printf("Sampling down from %zu to %d points...\n", points.size(), numPoints); // Shuffle and resize std::shuffle(points.begin(), points.end(), gen); points.resize(numPoints); } // If we have too few points, we'll use what we have else if (points.size() < static_cast(numPoints)) { printf("Warning: Only generated %zu points (requested %d)\n", points.size(), numPoints); } return points; } // Alternative sphere generation with perlin-like noise for more natural wiggling std::vector generateSphereWithNaturalWiggle(int numPoints, float radius = 1.0f, float noiseStrength = 0.15f) { printf("Generating sphere with natural wiggling using %d points...\n", numPoints); printf("Noise strength: %.3f\n", noiseStrength); std::vector points; // Random number generators std::random_device rd; std::mt19937 gen(rd()); std::uniform_real_distribution dist(-1.0f, 1.0f); // Calculate grid resolution int gridRes = static_cast(std::cbrt(numPoints / 0.52f)) + 1; printf("Using grid resolution: %d x %d x %d\n", gridRes, gridRes, gridRes); float voxelSize = 2.0f / gridRes; float maxDisplacement = noiseStrength * voxelSize; // Pre-compute some random offsets for pseudo-perlin noise std::vector randomOffsets(gridRes * gridRes * gridRes); for (size_t i = 0; i < randomOffsets.size(); ++i) { randomOffsets[i] = dist(gen); } auto getNoise = [&](int x, int y, int z) -> float { int idx = (x * gridRes * gridRes) + (y * gridRes) + z; return randomOffsets[idx % randomOffsets.size()]; }; for (int x = 0; x < gridRes; ++x) { for (int y = 0; y < gridRes; ++y) { for (int z = 0; z < gridRes; ++z) { Vec3 point( (2.0f * x / (gridRes - 1)) - 1.0f, (2.0f * y / (gridRes - 1)) - 1.0f, (2.0f * z / (gridRes - 1)) - 1.0f ); if (point.lengthSquared() <= 1.0f) { // Use smoother noise for more natural displacement float noiseX = getNoise(x, y, z); float noiseY = getNoise(y, z, x); float noiseZ = getNoise(z, x, y); // Apply displacement that preserves the overall spherical structure Vec3 displacement( noiseX * maxDisplacement, noiseY * maxDisplacement, noiseZ * maxDisplacement ); Vec3 displacedPoint = point + displacement; // Ensure we don't push points too far from sphere surface float currentLength = displacedPoint.length(); if (currentLength > 1.0f) { displacedPoint = displacedPoint * (0.95f + 0.05f * dist(gen)); // Small random scaling } points.push_back(displacedPoint * radius); } } } } printf("Generated %zu points with natural wiggling\n", points.size()); // Sample down if needed if (points.size() > static_cast(numPoints)) { printf("Sampling down from %zu to %d points...\n", points.size(), numPoints); std::shuffle(points.begin(), points.end(), gen); points.resize(numPoints); } else if (points.size() < static_cast(numPoints)) { printf("Warning: Only generated %zu points (requested %d)\n", points.size(), numPoints); } return points; } // Modified function to populate voxel grid with sphere points and assign layers void populateVoxelGridWithLayeredSphere(VoxelGrid& grid, const std::vector& points) { printf("Populating voxel grid with %zu sphere points...\n", points.size()); // First add all voxels with a default color Vec4 defaultColor(1.0f, 1.0f, 1.0f, 1.0f); // Temporary color for (const auto& point : points) { grid.addVoxel(point, defaultColor); } printf("Voxel grid populated with %zu voxels\n", grid.getOccupiedPositions().size()); // Now assign the planetary layers grid.assignPlanetaryLayers(); } Vec3 rotateX(const Vec3& point, float angle) { float cosA = std::cos(angle); float sinA = std::sin(angle); return Vec3( point.x, point.y * cosA - point.z * sinA, point.y * sinA + point.z * cosA ); } Vec3 rotateY(const Vec3& point, float angle) { float cosA = std::cos(angle); float sinA = std::sin(angle); return Vec3( point.x * cosA + point.z * sinA, point.y, -point.x * sinA + point.z * cosA ); } Vec3 rotateZ(const Vec3& point, float angle) { float cosA = std::cos(angle); float sinA = std::sin(angle); return Vec3( point.x * cosA - point.y * sinA, point.x * sinA + point.y * cosA, point.z ); } void visualizePointCloud(const std::vector& points, const std::vector& colors, const std::string& filename, int width = 1000, int height = 1000, float angleX = 0.0f, float angleY = 0.0f, float angleZ = 0.0f) { TIME_FUNCTION; std::vector pixels(width * height * 3, 0); // Background color (dark blue) for (int i = 0; i < width * height * 3; i += 3) { pixels[i] = 30; // B pixels[i + 1] = 30; // G pixels[i + 2] = 50; // R } // Find bounds of point cloud Vec3 minPoint( std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max()); Vec3 maxPoint(-std::numeric_limits::max(), -std::numeric_limits::max(), -std::numeric_limits::max()); // Apply rotation to all points and find new bounds std::vector rotatedPoints; rotatedPoints.reserve(points.size()); for (const auto& point : points) { Vec3 rotated = point; rotated = rotateX(rotated, angleX); rotated = rotateY(rotated, angleY); rotated = rotateZ(rotated, angleZ); rotatedPoints.push_back(rotated); minPoint.x = std::min(minPoint.x, rotated.x); minPoint.y = std::min(minPoint.y, rotated.y); minPoint.z = std::min(minPoint.z, rotated.z); maxPoint.x = std::max(maxPoint.x, rotated.x); maxPoint.y = std::max(maxPoint.y, rotated.y); maxPoint.z = std::max(maxPoint.z, rotated.z); } Vec3 cloudSize = maxPoint - minPoint; float maxDim = std::max({cloudSize.x, cloudSize.y, cloudSize.z}); // Draw points for (size_t i = 0; i < rotatedPoints.size(); i++) { const auto& point = rotatedPoints[i]; const auto& color = colors[i]; // Map 3D point to 2D screen coordinates (orthographic projection) int screenX = static_cast(((point.x - minPoint.x) / maxDim) * (width - 20)) + 10; int screenY = static_cast(((point.y - minPoint.y) / maxDim) * (height - 20)) + 10; if (screenX >= 0 && screenX < width && screenY >= 0 && screenY < height) { uint8_t r, g, b; color.toUint8(r, g, b); // Draw a 2x2 pixel for each point for (int dy = -1; dy <= 1; dy++) { for (int dx = -1; dx <= 1; dx++) { int px = screenX + dx; int py = screenY + dy; if (px >= 0 && px < width && py >= 0 && py < height) { int index = (py * width + px) * 3; pixels[index] = b; pixels[index + 1] = g; pixels[index + 2] = r; } } } } } BMPWriter::saveBMP(filename, pixels, width, height); } int main() { printf("=== Layered Sphere Generation and Visualization ===\n\n"); const int numPoints = 100000000; const float radius = 2.0f; printf("Generating layered spheres with %d points each, radius %.1f\n\n", numPoints, radius); // Create voxel grid VoxelGrid grid(Vec3(10, 10, 10), Vec3(0.1f, 0.1f, 0.1f)); // Generate sphere with different wiggle options: // Option 1: Simple random wiggling (small amount - 10% of voxel size) printf("1. Generating sphere with simple wiggling...\n"); auto sphere1 = generateSphereWithNaturalWiggle(numPoints, radius, 0.1f); populateVoxelGridWithLayeredSphere(grid, sphere1); // Extract positions and colors for visualization std::vector occupiedPositions = grid.getOccupiedPositions(); std::vector layerColors = grid.getColors(); // Convert to world coordinates for visualization std::vector worldPositions; for (const auto& gridPos : occupiedPositions) { worldPositions.push_back(grid.gridToWorld(gridPos)); } // Create multiple visualizations from different angles printf("\nGenerating views from different angles...\n"); // Front view (0, 0, 0) visualizePointCloud(worldPositions, layerColors, "output/sphere_front.bmp", 1000, 1000, 0.0f, 0.0f, 0.0f); printf(" - sphere_front.bmp (front view)\n"); // 45 degree rotation around Y axis visualizePointCloud(worldPositions, layerColors, "output/sphere_45y.bmp", 1000, 1000, 0.0f, M_PI/4, 0.0f); printf(" - sphere_45y.bmp (45° Y rotation)\n"); // 90 degree rotation around Y axis (side view) visualizePointCloud(worldPositions, layerColors, "output/sphere_side.bmp", 1000, 1000, 0.0f, M_PI/2, 0.0f); printf(" - sphere_side.bmp (side view)\n"); // 45 degree rotation around X axis (top-down perspective) visualizePointCloud(worldPositions, layerColors, "output/sphere_45x.bmp", 1000, 1000, M_PI/4, 0.0f, 0.0f); printf(" - sphere_45x.bmp (45° X rotation)\n"); // Combined rotation (30° X, 30° Y) visualizePointCloud(worldPositions, layerColors, "output/sphere_30x_30y.bmp", 1000, 1000, M_PI/6, M_PI/6, 0.0f); printf(" - sphere_30x_30y.bmp (30° X, 30° Y rotation)\n"); // Top view (90° X rotation) visualizePointCloud(worldPositions, layerColors, "output/sphere_top.bmp", 1000, 1000, M_PI/2, 0.0f, 0.0f); printf(" - sphere_top.bmp (top view)\n"); printf("\n=== Sphere generated successfully ===\n"); printf("Files created in output/ directory:\n"); printf(" - sphere_front.bmp (Front view)\n"); printf(" - sphere_45y.bmp (45° Y rotation)\n"); printf(" - sphere_side.bmp (Side view)\n"); printf(" - sphere_45x.bmp (45° X rotation)\n"); printf(" - sphere_30x_30y.bmp (30° X, 30° Y rotation)\n"); printf(" - sphere_top.bmp (Top view)\n"); return 0; }