1
0
Fork 0
Univerxel/src/modules/core/PlanetGenerator.hpp

119 lines
5.1 KiB
C++

#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 <PlanetShape PS>
class Planet final: public Abstract {
private:
template<uint32_t level>
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<double>(-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<float>((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<float>(0, 1.f - heightRatio * heightRatio) * glm::vec3(mass);
}
std::optional<double> 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<PlanetShape::Cube>;
using RoundPlanet = Planet<PlanetShape::Sphere>;
}