#ifndef PLANET_HPP #define PLANET_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include "../grid/grid3eigen.hpp" #include "../timing_decorator.cpp" #include "../imgui/imgui.h" #include "../imgui/backends/imgui_impl_glfw.h" #include "../imgui/backends/imgui_impl_opengl3.h" #include #include "../stb/stb_image.h" using v3 = Eigen::Vector3f; const float Φ = M_PI * (3.0f - std::sqrt(5.0f)); enum class PlateType { CONTINENTAL, OCEANIC, MIXED }; struct Particle { float noiseDisplacement = 0.0f; int plateID = -1; Eigen::Vector3f basePos; Eigen::Vector3f currentPos; float plateDisplacement = 0.0f; float temperature = -1; float water = -1; Eigen::Vector3f originColor; bool surface = false; //gravity factors: Eigen::Matrix velocity; Eigen::Matrix acceleration; Eigen::Matrix forceAccumulator; float density = 0.0f; float pressure = 0.0f; Eigen::Matrix pressureForce; float viscosity = 0.5f; Eigen::Matrix viscosityForce; float restitution = 5.0f; float mass; bool isStatic = false; float soundSpeed = 100.0f; std::unordered_map neighbors; }; struct planetConfig { Eigen::Vector3f center = Eigen::Vector3f(0,0,0); float radius = 1024.0f; Eigen::Vector3f color = Eigen::Vector3f(0, 1, 0); float voxelSize = 10.0f; int surfacePoints = 50000; int currentStep = 0; float displacementStrength = 200.0f; std::vector surfaceNodes; float noiseStrength = 1.0f; int numPlates = 15; float plateRandom = 0.6f; int smoothingPasses = 3; float mountHeight = 250.0f; float valleyDepth = -150.0f; float transformRough = 80.0f; int stressPasses = 5; float maxElevationRatio = 0.25f; float gridSizeCube = 16384; float SMOOTHING_RADIUS = 1024.0f; float REST_DENSITY = 0.00005f; float TIMESTEP = 0.016f; float G_ATTRACTION = 50.0f; float gravitySoftening = 10.0f; float pressureStiffness = 50000.0f; float coreRepulsionRadius = 1000.0f; float coreRepulsionStiffness = 100000.0f; float dampingFactor = 0.98f; }; struct PlateConfig { int plateId; Eigen::Vector3f plateEulerPole; Eigen::Vector3f direction; float angularVelocity; float thickness; float density; float rigidity; float temperature; Eigen::Vector3f debugColor; PlateType ptype; }; class planetsim { public: planetConfig config; Octree grid; std::vector plates; std::mt19937 rng = std::mt19937(42); planetsim() { config = planetConfig(); grid = Octree({-config.gridSizeCube,-config.gridSizeCube,-config.gridSizeCube,},{config.gridSizeCube,config.gridSizeCube,config.gridSizeCube}); } float evaluate2DStack(const Eigen::Vector2f& point, const NoisePreviewState& state, PNoise2& gen) { float finalValue = 0.0f; Eigen::Vector2f p = point; for (const auto& layer : state.layers) { if (!layer.enabled) continue; Eigen::Vector2f samplePoint = p * layer.scale; samplePoint += Eigen::Vector2f((float)layer.seedOffset * 10.5f, (float)layer.seedOffset * -10.5f); if (layer.blend == BlendMode::DomainWarp) { if (layer.type == NoiseType::CurlNoise) { Eigen::Vector2f flow = gen.curlNoise(samplePoint); p += flow * layer.strength * 100.0f; } else { float warpX = sampleNoiseLayer(gen, layer.type, samplePoint, layer); float warpY = sampleNoiseLayer(gen, layer.type, samplePoint + Eigen::Vector2f(5.2f, 1.3f), layer); p += Eigen::Vector2f(warpX, warpY) * layer.strength * 100.0f; } continue; } float nVal = sampleNoiseLayer(gen, layer.type, samplePoint, layer); switch (layer.blend) { case BlendMode::Replace: finalValue = nVal * layer.strength; break; case BlendMode::Add: finalValue += nVal * layer.strength; break; case BlendMode::Subtract: finalValue -= nVal * layer.strength; break; case BlendMode::Multiply: finalValue *= (nVal * layer.strength); break; case BlendMode::Max: finalValue = std::max(finalValue, nVal * layer.strength); break; case BlendMode::Min: finalValue = std::min(finalValue, nVal * layer.strength); break; } } float norm = std::tanh(finalValue); return norm; } void generateFibSphere() { TIME_FUNCTION; grid.clear(); config.surfaceNodes.clear(); for (int i = 0; i < config.surfacePoints; i++) { float y = 1.0f - (i * 2.0f) / (config.surfacePoints - 1); float radiusY = std::sqrt(1.0f- y * y); float Θ = Φ * i; float x = std::cos(Θ) * radiusY; float z = std::sin(Θ) * radiusY; v3 dir(x, y, z); v3 pos = config.center + dir * config.radius; Particle pt; pt.basePos = pos; pt.currentPos = pos; pt.originColor = config.color; pt.noiseDisplacement = 0.0f; pt.surface = true; config.surfaceNodes.emplace_back(pt); grid.set(pt, pt.currentPos, true, pt.originColor, config.voxelSize, true, 1, 0, false, 0.0f, 0.0f, 0.0f); } config.currentStep = 1; std::cout << "Step 1 done. base sphere generated" << std::endl; buildAdjacencyList(); grid.save("output/fibSphere"); } inline void _applyNoise(std::function noiseFunc) { for (auto& p : config.surfaceNodes) { Eigen::Vector3f oldPos = p.currentPos; float displacementValue = noiseFunc(p.basePos); p.noiseDisplacement = displacementValue; Eigen::Vector3f normal = p.basePos.normalized(); p.currentPos = p.basePos + (normal * displacementValue * config.displacementStrength); grid.update(oldPos, p.currentPos, p, true, p.originColor, config.voxelSize, true, -2, false, 0.0f, 0.0f, 0.0f); } } void assignSeeds() { plates.clear(); plates.resize(config.numPlates); float sphereSurfaceArea = 4.0f * M_PI * config.radius * config.radius; float averageAreaPerPlate = sphereSurfaceArea / config.numPlates; float minDistance = std::sqrt(averageAreaPerPlate) * 0.4f; std::vector selectedSeedIndices; std::uniform_int_distribution distNode(0, config.surfaceNodes.size() - 1); for (int i = 0; i < config.numPlates; ++i) { int attempts = 1000; bool foundValidSeed = false; int seedid = distNode(rng); plates[i].plateId = i; while (!foundValidSeed && attempts > 0) { int seedIndex = distNode(rng); bool tooClose = false; for (int selectedIndex : selectedSeedIndices) { const auto& existingSeed = config.surfaceNodes[selectedIndex]; const auto& candidateSeed = config.surfaceNodes[seedIndex]; float dot = existingSeed.basePos.normalized().dot(candidateSeed.basePos.normalized()); float angle = std::acos(std::clamp(dot, -1.0f, 1.0f)); float distanceOnSphere = angle * config.radius; if (distanceOnSphere < minDistance) { tooClose = true; break; } } if (!tooClose || selectedSeedIndices.empty()) { selectedSeedIndices.push_back(seedIndex); plates[i].plateId = i; config.surfaceNodes[seedIndex].plateID = i; float colorVal = static_cast(seedid) / config.surfaceNodes.size(); plates[i].debugColor = v3(colorVal,colorVal,colorVal); foundValidSeed = true; } attempts--; } if (!foundValidSeed) { int seedIndex = distNode(rng); selectedSeedIndices.push_back(seedIndex); plates[i].plateId = i; float colorVal = static_cast(seedIndex) / config.surfaceNodes.size(); plates[i].debugColor = v3(colorVal,colorVal,colorVal); config.surfaceNodes[seedIndex].plateID = i; } } } void buildAdjacencyList() { TIME_FUNCTION; for (int i = 0; i < config.surfaceNodes.size(); i++) { Particle in = config.surfaceNodes[i]; v3 inn = in.basePos.normalized(); for (int j = 1; j < config.surfaceNodes.size(); j++) { if (i == j) { continue; } auto ij = config.surfaceNodes[j]; if (ij.neighbors.contains(i)){ in.neighbors[j] = ij.neighbors[i]; } v3 ijn = ij.basePos.normalized(); float cosangle = inn.dot(ijn); float angle = std::acos(cosangle); in.neighbors[j] = angle; } } } void growPlatesRandom() { TIME_FUNCTION; } void growPlatesCellular() { TIME_FUNCTION; } void fixBoundaries() { TIME_FUNCTION; } void extraplateste() { } void boundaryStress() { TIME_FUNCTION; } void finalizeApplyResults() { TIME_FUNCTION; float maxAllowedDisp = config.radius * config.maxElevationRatio; for (auto& p : config.surfaceNodes) { Eigen::Vector3f oldPos = p.currentPos; grid.update(oldPos, p.currentPos, p, true, plates[p.plateID].debugColor, config.voxelSize, true, -2, false, 0.0f, 0.0f, 0.0f); } std::cout << "Finalize apply results completed." << std::endl; } }; #endif