164 lines
7.3 KiB
C++
164 lines
7.3 KiB
C++
#pragma once
|
|
|
|
#include <variant>
|
|
#include "Noise.hpp"
|
|
#include "../../core/world/Voxel.hpp"
|
|
#include "../../core/data/math.hpp"
|
|
|
|
namespace world::generator {
|
|
|
|
/// Abstract Noise generator
|
|
class Abstract {
|
|
public:
|
|
/// Generate chunk voxels
|
|
/// MAYBE: use template to avoid virtual
|
|
virtual void generate(const chunk_pos &at, std::array<Voxel, CHUNK_SIZE> &out) = 0;
|
|
/// Get gravity vector at given point
|
|
virtual glm::vec3 getGravity(const voxel_pos& point) const = 0;
|
|
};
|
|
|
|
// Generate empty space
|
|
class Void: public Abstract {
|
|
public:
|
|
struct Params { };
|
|
Void() = default;
|
|
|
|
void generate(const chunk_pos &, std::array<Voxel, CHUNK_SIZE> &out) override {
|
|
out.fill(Voxel());
|
|
}
|
|
glm::vec3 getGravity(const voxel_pos&) const override { return glm::vec3(0); }
|
|
};
|
|
|
|
/// Endless cave network
|
|
class Cave: public Abstract {
|
|
public:
|
|
struct Params {
|
|
Params(int seed = 42, float density = 0, float gran = 30): seed(seed), density(density), granularity(gran) { }
|
|
|
|
/// Random generator start
|
|
int seed;
|
|
/// Offset density overrage
|
|
float density;
|
|
/// Speed of density change
|
|
float granularity;
|
|
};
|
|
Cave(const Params p): params(p), density(Noise::SimplexFractal(p.seed)), material(Noise::Cellular(p.seed * 5, .1)) { }
|
|
|
|
void generate(const chunk_pos &pos, std::array<Voxel, CHUNK_SIZE> &out) override {
|
|
auto densitySet = density.Get(pos, CHUNK_LENGTH);
|
|
auto materialSet = material.Get(pos, CHUNK_LENGTH);
|
|
for (size_t i = 0; i < CHUNK_SIZE; i++) {
|
|
const auto density = std::clamp((densitySet.get()[i] + params.density) * params.granularity, 0.f, 1.f) * Voxel::DENSITY_MAX;
|
|
const auto material = density > 0 ? 2 + std::clamp(static_cast<int>(std::lrint((materialSet.get()[i] + 1) / 2 * (materials::count - 3))),
|
|
0, materials::count - 3) : 1; //NOTE: map (approx -1, 1) to (1, mat_max)
|
|
out[i] = Voxel(material, density);
|
|
}
|
|
}
|
|
glm::vec3 getGravity(const voxel_pos&) const override { return glm::vec3(-1, 0, 0); }
|
|
private:
|
|
Params params;
|
|
Noise density;
|
|
Noise material;
|
|
};
|
|
|
|
enum class PlanetShape { Cube, Sphere };
|
|
|
|
/// Abstract shaped planet generator
|
|
template <PlanetShape PS>
|
|
class Planet: public Abstract {
|
|
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 generate(const chunk_pos &pos, std::array<Voxel, CHUNK_SIZE> &out) override {
|
|
auto densitySet = density.Get(pos, CHUNK_LENGTH);
|
|
auto displacementSet = displacement.Get(pos, CHUNK_LENGTH);
|
|
for (size_t i = 0; i < CHUNK_SIZE; i++) {
|
|
const auto heightRatio = static_cast<float>(getHeight(glm::multiply(pos) + glm::llvec3(glm::fromIdx(i))) - params.height) / params.height;
|
|
|
|
const auto verticalDensityOffset = heightRatio / (heightRatio >= 0 ? params.surface_roughness : params.depth_roughness);
|
|
const auto density = std::clamp((densitySet.get()[i] + params.density - verticalDensityOffset) * params.granularity, 0.f, 1.f) * Voxel::DENSITY_MAX;
|
|
|
|
out[i] = [&]() -> Voxel {
|
|
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) ? materials::GRASS : materials::DIRT;
|
|
} else {
|
|
return noisedHeightRatio >= -params.beach_depth ? materials::SAND : materials::ROCK;
|
|
}
|
|
}();
|
|
return Voxel(material, density);
|
|
} else {
|
|
if (heightRatio >= 0) {
|
|
return Voxel(materials::AIR, (heightRatio < params.surface_roughness) * Voxel::DENSITY_MAX);
|
|
} else {
|
|
return Voxel(materials::WATER, std::clamp(-heightRatio * params.height, 0.f, 1.f) * Voxel::DENSITY_MAX);
|
|
}
|
|
}
|
|
}();
|
|
}
|
|
}
|
|
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);
|
|
}
|
|
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>;
|
|
|
|
using params = std::variant<Void::Params, Cave::Params, CubicPlanet::Params, RoundPlanet::Params>;
|
|
inline std::unique_ptr<Abstract> load(const params &p) {
|
|
const auto get_new = [](const params &p) -> Abstract* {
|
|
if(std::holds_alternative<Void::Params>(p)) {
|
|
return new Void();
|
|
} else if(const auto c = std::get_if<Cave::Params>(&p)) {
|
|
return new Cave(*c);
|
|
} else if(const auto pc = std::get_if<CubicPlanet::Params>(&p)) {
|
|
return new CubicPlanet(*pc);
|
|
} else if(const auto ps = std::get_if<RoundPlanet::Params>(&p)) {
|
|
return new RoundPlanet(*ps);
|
|
}
|
|
return nullptr;
|
|
};
|
|
return std::unique_ptr<Abstract>(get_new(p));
|
|
}
|
|
} |