#pragma once #include "core/world/generator/Cave.hpp" #include "core/geometry/math.hpp" #include "./Core.hpp" namespace world::generator { enum class PlanetShape { Cube, Sphere }; /// Abstract shaped planet generator template class Planet final: public Abstract { private: template inline void generate(const voxel_pos& min, voxels& out) const { constexpr uint32_t LENGTH = 1u << level; constexpr size_t SIZE = glm::pow3(LENGTH); constexpr auto VEC = glm::i32vec3(LENGTH); const auto densitySet = density.getRaw(min, VEC); const auto displacementSet = displacement.getRaw(min, VEC); const auto& IDS = world::module::core::Core::Get()->ids; const auto genAt = [&](size_t i) -> Voxel { const auto heightRatio = ((float)getHeight(min + voxel_pos(glm::fromIdx(i))) - params.height) / params.height; const auto verticalDensityOffset = heightRatio / (heightRatio >= 0 ? params.surface_roughness : params.depth_roughness); const auto density = std::min((densitySet.get()[i] + params.density - verticalDensityOffset) * params.granularity, 1.f) * Voxel::DENSITY_MAX; if (density > 0) { const auto material = [&]() -> int { const auto noisedHeightRatio = heightRatio - displacementSet.get()[i] * params.beach_displacement; if(noisedHeightRatio >= 0) { return densitySet.get()[i] + params.density < ((heightRatio + 0.007f) / params.surface_roughness) ? IDS.GRASS : IDS.DIRT; } else { return noisedHeightRatio >= -params.beach_depth ? IDS.SAND : IDS.ROCK; } }(); return Voxel(material, density); } else { if (heightRatio >= 0) { return Voxel(IDS.AIR, (heightRatio < params.surface_roughness) * Voxel::DENSITY_MAX); } else { return Voxel(IDS.WATER, std::clamp(-heightRatio * params.height, 0.f, 1.f) * Voxel::DENSITY_MAX); } } }; const auto sample = genAt(0); bool unique = true; out.resize(SIZE); for (size_t i = 0; i < SIZE; i++) { out[i] = genAt(i); if (unique && out[i].value != sample.value) unique = false; } if (unique) { out = voxels{sample}; } } public: struct Params: Cave::Params { Params(voxel_pos::value_type height, int seed = 42, float surface_roughness = .1, float depth_roughness = .05, float density = 0, float gran = 30): Cave::Params(seed, density, gran), height(height), surface_roughness(surface_roughness), depth_roughness(depth_roughness) { } /// Sea level (minimal density) voxel_pos::value_type height; /// Density decrease over height float surface_roughness; /// Density decrease under height float depth_roughness; /// Sea border depth float beach_depth = .01f; /// Sea ground displacement float beach_displacement = .01f; }; Planet(const Params& p) : params(p), density(Noise::SimplexFractal(p.seed)), displacement(Noise::Simplex(p.seed * 5, .01)) {} void generate5(const voxel_pos &min, voxels &out) override { generate<5>(min, out); } glm::vec3 getGravity(const voxel_pos& pos) const override { const auto heightRatio = static_cast((getHeight(pos) - params.height) / params.height); const auto scale = params.height >> 7; const auto mass = scale * scale * scale; //FIXME: average material density return -getSurfaceDir(pos) * std::max(0, 1.f - heightRatio * heightRatio) * glm::vec3(mass); } std::optional getCurvature() const override { if constexpr (shape == PlanetShape::Cube) { return params.height * (1.1 + params.surface_roughness); } return {}; } private: static constexpr PlanetShape shape = PS; static constexpr glm::f64 getHeight(const voxel_pos &p) { if constexpr(shape == PlanetShape::Cube) { return glm::max_axis(glm::abs(p)); } else { return glm::length(glm::dvec3(p)); } } /// Centre inverse direction // NOTE: So center point is consider mass center static constexpr glm::vec3 getSurfaceDir(const voxel_pos &p) { if constexpr(shape == PlanetShape::Cube) { return glm::inormalize(p); } else { return glm::normalize(glm::dvec3(p)); } } Params params; Noise density; Noise displacement; }; using CubicPlanet = Planet; using RoundPlanet = Planet; }