Commit 5ac3a003 authored by Shu's avatar Shu 🍠
Browse files

Refactor generators

parent b7669df9
......@@ -29,13 +29,13 @@
- [x] Offset
- [ ] Rotation
- [~] Planet
- Render
- [x] CubeSphere
- [~] CubeSphere
- [ ] Area corrected CubeSphere
- [ ] Corrected Normals
- [ ] Surface curvature
- [ ] Curvature avare frustum
- [ ] Healpix
- [ ] Sphere
- [ ] Healpix
- [ ] Surface features
- [ ] Biomes
- https://imgur.com/kM8b5Zq
......@@ -78,7 +78,8 @@
- [ ] Environment mapping
- [ ] Deferred
- [ ] Cascaded shadow maps
- [ ] RayCast
- [ ] Vulkan
- [ ] Ray Tracing
- [~] Transparency
- [x] Float precision problem
......@@ -98,5 +99,7 @@
- [ ] Render with glBufferSubData
- [x] Frustum Culling
- [~] Occlusion Culling
- Primitive raycast
- [x] Primitive raycast
- [ ] Iterator ray
- [ ] Cast from chunk center
- [x] Document
\ No newline at end of file
......@@ -15,11 +15,13 @@ namespace contouring {
std::get<0>(it_a->second.first) = area->second->getOffset();
std::get<1>(it_a->second.first) = area->second->getChunks().getRadius() * static_cast<long long>(CHUNK_LENGTH);
const auto centerV = pos - std::get<0>(it_a->second.first).as_voxel();
if (auto planet = std::get_if<world::Generator::Properties::planet>(
&area->second->getGenerator().getProperties().shape)) {
const auto dist = glm::max_axis(glm::abs(centerV));
const auto surface = planet->height * planet->surface_roughness;
std::get<2>(it_a->second.first) = std::clamp((dist - surface) / (std::get<1>(it_a->second.first) - surface), -0.1f, 1.f);
{
const auto params = area->second->getParams().properties;
if (auto planet = std::get_if<world::generator::CubicPlanet::Params>(&params)) {
const auto dist = glm::max_axis(glm::abs(centerV));
const auto surface = planet->height * planet->surface_roughness;
std::get<2>(it_a->second.first) = std::clamp((dist - surface) / (std::get<1>(it_a->second.first) - surface), -0.1f, 1.f);
}
}
const auto center = glm::divide(centerV);
auto &bfs = it_a->second.second;
......
......@@ -136,11 +136,13 @@ namespace contouring {
std::get<0>(it_a->second.first) = area->second->getOffset();
std::get<1>(it_a->second.first) = area->second->getChunks().getRadius() * static_cast<long long>(CHUNK_LENGTH);
const auto centerV = pos - std::get<0>(it_a->second.first).as_voxel();
if (auto planet = std::get_if<world::Generator::Properties::planet>(
&area->second->getGenerator().getProperties().shape)) {
const auto dist = glm::max_axis(glm::abs(centerV));
const auto surface = planet->height * (1.1+planet->surface_roughness);
std::get<2>(it_a->second.first) = std::clamp<float>((dist - surface) / (std::get<1>(it_a->second.first) - surface), -0.1f, 1.f);
{
const auto params = area->second->getParams().properties;
if (auto planet = std::get_if<world::generator::CubicPlanet::Params>(&params)) {
const auto dist = glm::max_axis(glm::abs(centerV));
const auto surface = planet->height * (1.1+planet->surface_roughness);
std::get<2>(it_a->second.first) = std::clamp<float>((dist - surface) / (std::get<1>(it_a->second.first) - surface), -0.1f, 1.f);
}
}
const auto center = glm::divide(centerV);
auto &bfs = it_a->second.second;
......
......@@ -96,7 +96,7 @@ namespace contouring {
void FlatSurroundingBox::render(const surrounding::faces &surrounding, std::vector<buffer::PackedVertexData> &vertices) {
const auto center = surrounding[surrounding::CENTER];
vertices.clear();
for (ushort i = 0; i < world::CHUNK_SIZE; i++) {
for (ushort i = 0; i < CHUNK_SIZE; i++) {
if (center->get(i).is_solid()) {
Faces faces = !center->get(i).is_full() ? Faces::All :
(isTransparent(surrounding, surrounding::getNeighborIdx(i, Face::Right)) & Faces::Right) |
......
......@@ -27,15 +27,14 @@ namespace contouring::box {
};
static void addQuad(std::vector<buffer::PackedVertexData> & out, glm::vec3 position, ushort material, Face face, glm::vec3 size) {
constexpr auto HALF_MANTISSA = 10;
for (auto vertex : g_quad_vertices) {
const auto p = glm::vec3(g_cube_rotate[static_cast<int>(face)] * glm::vec4(vertex, 1)) * size + position;
out.emplace_back(meshopt_quantizeHalf(p.x), meshopt_quantizeHalf(p.y),
meshopt_quantizeHalf(p.z), material,
meshopt_quantizeHalf(g_cube_normals[static_cast<int>(face)].x), meshopt_quantizeHalf(g_cube_normals[static_cast<int>(face)].y),
meshopt_quantizeHalf(g_cube_normals[static_cast<int>(face)].z));
}
for (auto vertex : g_quad_vertices) {
const auto p = glm::vec3(g_cube_rotate[static_cast<int>(face)] * glm::vec4(vertex, 1)) * size + position;
out.emplace_back(meshopt_quantizeHalf(p.x), meshopt_quantizeHalf(p.y),
meshopt_quantizeHalf(p.z), material,
meshopt_quantizeHalf(g_cube_normals[static_cast<int>(face)].x), meshopt_quantizeHalf(g_cube_normals[static_cast<int>(face)].y),
meshopt_quantizeHalf(g_cube_normals[static_cast<int>(face)].z));
}
}
static void addCube(std::vector<buffer::PackedVertexData>& out, glm::vec3 position, uint material, Faces faces = Faces::All, glm::vec3 size = glm::vec3(1)) {
if (faces && Faces::Right)
......
......@@ -70,4 +70,9 @@ namespace glm {
constexpr T inline max_axis(const vec<3, T> &v) {
return std::max({v.x, v.y, v.z});
}
ivec3 inline inormalize(const ivec3 &v) {
const auto a = glm::abs(v);
return a.x >= a.y ? (a.x >= a.z ? ivec3(glm::sign(v.x), 0, 0) : ivec3(0, 0, glm::sign(v.z))) :
(a.y >= a.z ? ivec3(0, glm::sign(v.y), 0) : ivec3(0, 0, glm::sign(v.z)));
}
}
......@@ -87,6 +87,8 @@ UI::Actions UI::draw(options &options, state &state, const reports &reports, GLu
ImGui::SameLine();
if(options.renderer.main.curvature) {
changeRenderer |= ImGui::Checkbox("Depth", &options.renderer.main.curv_depth);
if(ImGui::IsItemHovered())
ImGui::SetTooltip("Planets may look way bigger than expected without it");
} else {
ImGui::TextDisabled("Depth");
}
......
......@@ -4,7 +4,7 @@
#include <shared_mutex_guarded.h>
using namespace libguarded;
#include "region/index.hpp"
#include "Generator.hpp"
#include "generator.hpp"
#include "../data/geometry/IBox.hpp"
namespace world {
......@@ -34,11 +34,11 @@ namespace world {
struct params {
voxel_pos center;
int radius;
Generator::Properties properties;
generator::params properties;
};
/// radius: size in chunk (length = radius * 2 + 1)
Area(const params& p): center(p.center), chunks(p.radius), generator(p.properties) { }
Area(const params& p): center(p.center), chunks(p.radius), generator(generator::load(p.properties)), generator_properties(p.properties) { }
inline const area_pos &getOffset() const { return center; }
inline geometry::IBox getBounding() const {
......@@ -59,9 +59,9 @@ namespace world {
std::shared_ptr<Region> getRegion(const std::string& folderPath, const area_<region_pos> &);
shared_guarded<regions_t>::handle getRegions() { return regions.lock(); }
inline Generator &getGenerator() { return generator; }
inline const std::unique_ptr<generator::Abstract> &getGenerator() { return generator; }
inline params getParams() const { return params{center.as_voxel(), chunks.getRadius(), generator.getProperties()}; }
inline params getParams() const { return params{center.as_voxel(), chunks.getRadius(), generator_properties}; }
private:
area_pos center;
......@@ -70,6 +70,7 @@ namespace world {
ChunkContainer chunks;
shared_guarded<regions_t> regions;
Generator generator;
std::unique_ptr<generator::Abstract> generator;
const generator::params generator_properties; //MAYBE: useless copy
};
}
\ No newline at end of file
#include "Chunk.hpp"
#include "materials.hpp"
#include <algorithm>
#include "../data/math.hpp"
using namespace world;
Chunk::Chunk(const chunk_pos& pos, Generator& rnd) {
const auto &p = rnd.getProperties();
if(!(std::holds_alternative<Generator::Properties::cheese>(p.shape) || std::holds_alternative<Generator::Properties::planet>(p.shape)))
return;
const auto &cheese = std::holds_alternative<Generator::Properties::cheese>(p.shape) ? std::get<Generator::Properties::cheese>(p.shape) :
std::get<Generator::Properties::planet>(p.shape);
const auto [densitySet, materialSet] = rnd.getChunk(pos, CHUNK_LENGTH);
const auto verticalDensity = [p, pos](glm::idx i){
if(const auto planet = std::get_if<Generator::Properties::planet>(&p.shape)) {
const auto heightRatio = static_cast<float>(glm::max_axis(glm::abs(glm::multiply(pos) + glm::llvec3(glm::fromIdx(i)))) - planet->height) / planet->height;
return heightRatio / (heightRatio >= 0 ? planet->surface_roughness : planet->cave_depth);
}
return 0.f;
};
for (size_t i = 0; i < CHUNK_SIZE; i++) {
const auto density = std::clamp((densitySet[i] + cheese.density - verticalDensity(i)) * cheese.granularity, 0.f, 1.f) * Voxel::DENSITY_MAX;
const auto material = density > 0 ? 1 + std::clamp(static_cast<int>(std::lrint((materialSet[i] + 1) / 2 * (materials::count - 2))),
0, materials::count - 2) : 0; //NOTE: map (approx -1, 1) to (1, mat_max)
voxels[i] = Voxel(material, density);
}
rnd.free(densitySet);
rnd.free(materialSet);
Chunk::Chunk(const chunk_pos& pos, const std::unique_ptr<generator::Abstract>& rnd) {
rnd->generate(pos, voxels);
}
#include <iostream>
Chunk::Chunk(std::istream& str, bool rle) {
......
......@@ -2,23 +2,20 @@
#include <memory>
#include <sstream>
#include "Generator.hpp"
#include "generator.hpp"
#include "Voxel.hpp"
#include "../data/geometry/Faces.hpp"
#include "../data/math.hpp"
/// Chunk length
namespace world {
const auto CHUNK_LENGTH2 = CHUNK_LENGTH * CHUNK_LENGTH;
const auto CHUNK_SIZE = CHUNK_LENGTH2 * CHUNK_LENGTH;
constexpr auto RLE = true; //NOTE: only 2.7% gain after zstd
constexpr auto RLE = true; //NOTE: only ~2.5% gain after zstd
using namespace geometry;
/// World part as linear 3d voxel array
class Chunk {
public:
Chunk() {}
Chunk(const chunk_pos& pos, Generator& rnd);
Chunk(const chunk_pos &pos, const std::unique_ptr<generator::Abstract> &rnd);
Chunk(std::istream& str, bool rle = RLE);
~Chunk();
......
#pragma once
#if HASTY
#include <hastyNoise.h>
#else
#include <FastNoiseSIMD.h>
#endif
#include <tuple>
#include <variant>
#include "position.h"
namespace world {
/// Noise generator
class Generator {
public:
/// Generation caracteristics
struct Properties {
/// Default cheese
Properties(int seed = 42): shape(cheese()), density(noise(seed)), material(noise(seed * 5, .1)) { }
/// Default planet
Properties(voxel_pos::value_type height, int seed = 42): shape(planet(height)), density(seed), material(seed * 5, .1) { }
/// Endless cave network
struct cheese {
cheese(float density = 0, float gran = 30): density(density), granularity(gran) { }
/// Offset density overrage
float density;
/// Speed of density change
float granularity;
};
// Cube planet
struct planet: cheese {
planet(voxel_pos::value_type height, float surface_roughness = .1, float cave_depth = .2, float density = 0, float gran = 30): cheese(density, gran),
height(height), surface_roughness(surface_roughness), cave_depth(cave_depth) { }
/// Sea level (minimal density)
voxel_pos::value_type height;
/// Density decrease over height
float surface_roughness;
/// Density decrease under height
float cave_depth;
};
std::variant<cheese, planet> shape;
struct noise {
noise(int seed, float freq = .01): seed(seed), frequency(freq) { }
int seed;
float frequency;
};
noise density;
noise material;
};
using set = float *;
#if HASTY
using noise = std::unique_ptr<HastyNoise::NoiseSIMD>;
#else
using noise = FastNoiseSIMD *;
#endif
Generator(const Properties& properties): properties(properties) {
#if HASTY
printf("double!!!\n");
const size_t fastestSimd = HastyNoise::GetFastestSIMD();
densityNoise = HastyNoise::CreateNoise(seed, fastestSimd);
materialNoise = HastyNoise::CreateNoise(seed * 5, fastestSimd);
materialNoise->SetNoiseType(HastyNoise::NoiseType::Cellular); // NOTE: probably heavy
materialNoise->SetCellularReturnType(HastyNoise::CellularReturnType::Value);
materialNoise->SetCellularDistanceFunction(HastyNoise::CellularDistance::Natural);
materialNoise->SetFrequency(.1);
#else
densityNoise = FastNoiseSIMD::NewFastNoiseSIMD(properties.density.seed);
densityNoise->SetFrequency(properties.density.frequency);
materialNoise = FastNoiseSIMD::NewFastNoiseSIMD(properties.material.seed);
materialNoise->SetNoiseType(FastNoiseSIMD::Cellular); // NOTE: probably heavy
materialNoise->SetCellularReturnType(FastNoiseSIMD::CellValue);
materialNoise->SetCellularDistanceFunction(FastNoiseSIMD::Natural);
materialNoise->SetFrequency(properties.material.frequency);
#endif
}
~Generator() {
#if !HASTY
delete densityNoise;
delete materialNoise;
#endif
}
/// Get block of given size with index pos.
/// @note owning pointers, call free(set set)
inline std::pair<set, set> getChunk(const chunk_pos& pos, int size) {
auto pair = std::make_pair(
densityNoise->GetNoiseSet(pos.x * size, pos.y * size, pos.z * size, size, size, size),
materialNoise->GetNoiseSet(pos.x * size, pos.y * size, pos.z * size, size, size, size)
);
#if HASTY
return {pair.first.release(), pair.second.release()};
#else
return pair;
#endif
}
static inline void free(set set) {
#if HASTY
HastyNoise::SetDeleter()(set);
#else
FastNoiseSIMD::FreeNoiseSet(set);
#endif
}
constexpr inline const Properties& getProperties() const { return properties; }
private:
Properties properties;
noise densityNoise;
noise materialNoise;
};
}
\ No newline at end of file
#pragma once
#if HASTY
#include <hastyNoise.h>
#else
#include <FastNoiseSIMD.h>
#endif
#include <memory>
#include "position.h"
namespace world {
/// Noise handler
class Noise {
public:
#if HASTY
using handle = HastyNoise::NoiseSIMD;
using set = std::unique_ptr<float>;
#else
using handle = FastNoiseSIMD;
struct fast_set_deleter {
constexpr fast_set_deleter() noexcept = default;
void operator()(float* set) const {
FastNoiseSIMD::FreeNoiseSet(set);
}
};
using set = std::unique_ptr<float, fast_set_deleter>;
#endif
struct Base {
int seed = 1337;
float frequency = .01f;
};
struct Simplex: Base {
Simplex(int _seed, float _frequency) {
seed = _seed;
frequency = _frequency;
}
};
struct SimplexFractal: Simplex {
SimplexFractal(int seed, float frequency = .01f, int octaves = 3, float lacunarity = 2.0f, float gain = .5f):
Simplex(seed, frequency), octaves(octaves), lacunarity(lacunarity), gain(gain) { }
int octaves;
float lacunarity;
float gain;
//FractalType m_fractalType = FBM;
};
struct Cellular: Base {
Cellular(int _seed, float _frequency) {
seed = _seed;
frequency = _frequency;
}
//MAYBE: add options
};
Noise(Simplex s): Noise(s, type::Simplex) { }
Noise(SimplexFractal sf): Noise(sf, type::SimplexFractal) {
noise->SetFractalGain(sf.gain);
noise->SetFractalLacunarity(sf.lacunarity);
noise->SetFractalOctaves(sf.octaves);
//noise->SetFractalType();
}
Noise(Cellular c): Noise(c, type::Cellular) {
#if HASTY
noise->SetCellularReturnType(HastyNoise::CellularReturnType::Value);
noise->SetCellularDistanceFunction(HastyNoise::CellularDistance::Natural);
#else
noise->SetCellularReturnType(FastNoiseSIMD::CellValue);
noise->SetCellularDistanceFunction(FastNoiseSIMD::Natural);
#endif
}
/// Get block of given size with index pos.
inline set Get(const chunk_pos& pos, int size) {
#if HASTY
return noise->GetNoiseSet(pos.x * size, pos.y * size, pos.z * size, size, size, size);
#else
return std::unique_ptr<float, fast_set_deleter>(noise->GetNoiseSet(pos.x * size, pos.y * size, pos.z * size, size, size, size), fast_set_deleter());
#endif
}
private:
enum class type {
SimplexFractal, Simplex, Cellular
};
#if HASTY
static HastyNoise::NoiseType ConvertType(type t) {
switch(t) {
case type::Simplex:
return HastyNoise::NoiseType::Simplex;
case type::SimplexFractal:
return HastyNoise::NoiseType::SimplexFractal;
case type::Cellular:
return HastyNoise::NoiseType::Cellular;
}
return HastyNoise::NoiseType::Simplex;
}
#else
static FastNoiseSIMD::NoiseType ConvertType(type t) {
switch(t) {
case type::Simplex:
return FastNoiseSIMD::NoiseType::Simplex;
case type::SimplexFractal:
return FastNoiseSIMD::NoiseType::SimplexFractal;
case type::Cellular:
return FastNoiseSIMD::NoiseType::Cellular;
}
return FastNoiseSIMD::NoiseType::Simplex;
}
#endif
Noise(Base p, type t) {
#if HASTY
const size_t fastestSimd = HastyNoise::GetFastestSIMD();
noise = HastyNoise::CreateNoise(p.seed, fastestSimd);
#else
noise = std::unique_ptr<FastNoiseSIMD>(FastNoiseSIMD::NewFastNoiseSIMD(p.seed));
#endif
noise->SetNoiseType(ConvertType(t));
noise->SetFrequency(p.frequency);
}
std::unique_ptr<handle> noise;
};
}
......@@ -31,12 +31,12 @@ Universe::Universe(const Universe::options &options): dicts("content/zstd.dict"
while(!index.eof()) {
size_t id = UINT32_MAX;
index.read(reinterpret_cast<char *>(&id), sizeof(size_t));
Area::params params{voxel_pos(0), 0, Generator::Properties(0)};
Area::params params{voxel_pos(0), 0, generator::params()};
index.read(reinterpret_cast<char *>(&params.center.x), sizeof(voxel_pos::value_type));
index.read(reinterpret_cast<char *>(&params.center.y), sizeof(voxel_pos::value_type));
index.read(reinterpret_cast<char *>(&params.center.z), sizeof(voxel_pos::value_type));
index.read(reinterpret_cast<char *>(&params.radius), sizeof(int));
index.read(reinterpret_cast<char *>(&params.properties), sizeof(Generator::Properties));
index.read(reinterpret_cast<char *>(&params.properties), sizeof(generator::params));
[[maybe_unused]]
auto ok = tmp.emplace(id, params).second;
assert(ok && "Duplicated area");
......@@ -49,8 +49,9 @@ Universe::Universe(const Universe::options &options): dicts("content/zstd.dict"
LOG_E("No index file!!! Probably a new world...");
//TODO: generate universe
const auto radius = 1 << 4;
far_areas.emplace(Area::params{glm::multiply(voxel_pos(radius, 0, 0)), radius, Generator::Properties(radius * CHUNK_LENGTH * 3 / 4, 42)});
//far_areas.emplace(Area::params{voxel_pos(0), 1 << 20, Generator::Properties(42)});
far_areas.emplace(Area::params{glm::multiply(voxel_pos(radius, 0, 0)), radius,
generator::params(std::in_place_type<generator::CubicPlanet::Params>, radius * CHUNK_LENGTH * 3 / 4, 42)});
//far_areas.emplace(Area::params{voxel_pos(0), 1 << 20, generator::params(std::in_place_type<generator::Cave::Params>, 42)});
}
index.close();
}
......@@ -151,7 +152,7 @@ void Universe::saveAreas() const {
index.write(reinterpret_cast<char *>(&params.center.y), sizeof(voxel_pos::value_type));
index.write(reinterpret_cast<char *>(&params.center.z), sizeof(voxel_pos::value_type));
index.write(reinterpret_cast<char *>(&params.radius), sizeof(int));
index.write(reinterpret_cast<char *>(&params.properties), sizeof(Generator::Properties)); // MAYBE: use binary structured format (protobuf/messagebuf)
index.write(reinterpret_cast<char *>(&params.properties), sizeof(generator::params)); // MAYBE: use binary structured format (protobuf/msgpack)
};
for(const auto& area: areas) {
write(area.first, area.second->getParams());
......
#pragma once
#include <variant>
#include "Noise.hpp"
#include "materials.hpp"
#include "Voxel.hpp"
#include "../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;
};
// 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());
}
};
/// 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 ? 1 + std::clamp(static_cast<int>(std::lrint((materialSet.get()[i] + 1) / 2 * (materials::count - 2))),
0, materials::count - 2) : 0; //NOTE: map (approx -1, 1) to (1, mat_max)
out[i] = Voxel(material, density);
}
}
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 cave_depth = .2, float density = 0, float gran = 30):
Cave::Params(seed, density, gran), height(height), surface_roughness(surface_roughness), cave_depth(cave_depth) { }
/// Sea level (minimal density)
voxel_pos::value_type height;
/// Density decrease over height