From: Daniel Karbach Date: Sat, 3 Oct 2015 17:00:24 +0000 (+0200) Subject: more parameters for world generation X-Git-Url: http://git.localhorst.tv/?a=commitdiff_plain;h=75ebb9101c7aec9c16ef418b822c39e81889f66f;p=blank.git more parameters for world generation world generation changed, so previous world might look weird on transitions between old and new generation well, weirder than usual, anyway if that's possible well, it is, but probably not like this --- diff --git a/assets b/assets index b965c83..fc0d11c 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit b965c835cce06d762c9741f42dee34809bbf6a08 +Subproject commit fc0d11c95840498d7588908bc882941a4b7eaa2a diff --git a/src/app/app.cpp b/src/app/app.cpp index 4d2d2c0..98c7286 100644 --- a/src/app/app.cpp +++ b/src/app/app.cpp @@ -349,6 +349,34 @@ void AssetLoader::LoadBlockTypes(const std::string &set_name, BlockTypeRegistry type.collision = in.GetBool(); } else if (name == "collide_block") { type.collide_block = in.GetBool(); + } else if (name == "generate") { + type.generate = in.GetBool(); + } else if (name == "min_solidity") { + type.min_solidity = in.GetFloat(); + } else if (name == "mid_solidity") { + type.mid_solidity = in.GetFloat(); + } else if (name == "max_solidity") { + type.max_solidity = in.GetFloat(); + } else if (name == "min_humidity") { + type.min_humidity = in.GetFloat(); + } else if (name == "mid_humidity") { + type.mid_humidity = in.GetFloat(); + } else if (name == "max_humidity") { + type.max_humidity = in.GetFloat(); + } else if (name == "min_temperature") { + type.min_temperature = in.GetFloat(); + } else if (name == "mid_temperature") { + type.mid_temperature = in.GetFloat(); + } else if (name == "max_temperature") { + type.max_temperature = in.GetFloat(); + } else if (name == "min_richness") { + type.min_richness = in.GetFloat(); + } else if (name == "mid_richness") { + type.mid_richness = in.GetFloat(); + } else if (name == "max_richness") { + type.max_richness = in.GetFloat(); + } else if (name == "commonness") { + type.commonness = in.GetFloat(); } else if (name == "shape") { in.ReadIdentifier(shape_name); if (shape_name == "block") { diff --git a/src/server/ServerState.cpp b/src/server/ServerState.cpp index 9020aa0..8c3f7e6 100644 --- a/src/server/ServerState.cpp +++ b/src/server/ServerState.cpp @@ -21,7 +21,7 @@ ServerState::ServerState( , block_types() , world(block_types, wc) , spawn_index(world.Chunks().MakeIndex(wc.spawn, 3)) -, generator(gc) +, generator(gc, block_types) , chunk_loader(world.Chunks(), generator, ws) , skeletons() , spawner(world, skeletons, gc.seed) @@ -29,6 +29,7 @@ ServerState::ServerState( , loop_timer(16) { TextureIndex tex_index; env.loader.LoadBlockTypes("default", block_types, tex_index); + generator.Scan(); skeletons.LoadHeadless(); spawner.LimitSkeletons(1, skeletons.Size()); server.SetPlayerModel(skeletons[0]); diff --git a/src/standalone/MasterState.cpp b/src/standalone/MasterState.cpp index dccc0f9..6ead299 100644 --- a/src/standalone/MasterState.cpp +++ b/src/standalone/MasterState.cpp @@ -30,7 +30,7 @@ MasterState::MasterState( , manip(env, player.GetEntity()) , input(world, player, manip) , interface(config, env.keymap, input, *this) -, generator(gc) +, generator(gc, block_types) , chunk_loader(world.Chunks(), generator, save) , chunk_renderer(player.GetChunks()) , skeletons() @@ -41,6 +41,7 @@ MasterState::MasterState( TextureIndex tex_index; env.loader.LoadBlockTypes("default", block_types, tex_index); interface.SetInventorySlots(block_types.Size() - 1); + generator.Scan(); chunk_renderer.LoadTextures(env.loader, tex_index); chunk_renderer.FogDensity(wc.fog_density); skeletons.Load(); diff --git a/src/world/BlockType.hpp b/src/world/BlockType.hpp index bdc4721..0cae9c0 100644 --- a/src/world/BlockType.hpp +++ b/src/world/BlockType.hpp @@ -21,24 +21,44 @@ struct BlockType { glm::vec3 color; glm::vec3 outline_color; - // a string to display to the user + /// a string to display to the user std::string label; Block::Type id; - // light level that blocks of this type emit + /// light level that blocks of this type emit int luminosity; - // whether to draw + /// whether to draw bool visible; - // if true, stops light from propagating and fixes level to luminosity + /// if true, stops light from propagating and fixes level to luminosity bool block_light; - // whether to check for collisions at all + /// whether to check for collisions at all bool collision; - // if the block should be impenetrable + /// if the block should be impenetrable bool collide_block; + // generation properties + /// whether to use this block in generation at all + bool generate; + // min/mid/max points for the respective properties + // should all be in the (-1,1) range + float min_solidity; + float mid_solidity; + float max_solidity; + float min_humidity; + float mid_humidity; + float max_humidity; + float min_temperature; + float mid_temperature; + float max_temperature; + float min_richness; + float mid_richness; + float max_richness; + /// commonness factor, random chance is multiplied by this + float commonness; + struct Faces { bool face[Block::FACE_COUNT]; Faces &operator =(const Faces &other) noexcept { diff --git a/src/world/Generator.cpp b/src/world/Generator.cpp index c37c18b..f15a252 100644 --- a/src/world/Generator.cpp +++ b/src/world/Generator.cpp @@ -1,5 +1,7 @@ #include "Generator.hpp" +#include "BlockType.hpp" +#include "BlockTypeRegistry.hpp" #include "Chunk.hpp" #include "../rand/OctaveNoise.hpp" @@ -8,18 +10,37 @@ namespace blank { -Generator::Generator(const Config &config) noexcept -: solidNoise(config.seed) -, typeNoise(config.seed) -, stretch(1.0f/config.stretch) -, solid_threshold(config.solid_threshold) -// TODO: stable dynamic generator configuration -, space(0) -, light(13) -, solids({ 1, 4, 7, 10 }) { +namespace { + +struct Candidate { + const BlockType *type; + float threshold; + Candidate(const BlockType *type, float threshold) + : type(type), threshold(threshold) { } +}; + +std::vector candidates; + +} + +Generator::Generator(const Config &config, const BlockTypeRegistry &types) noexcept +: config(config) +, types(types) +, solidity_noise(config.seed ^ config.solidity.seed_mask) +, humidity_noise(config.seed ^ config.humidity.seed_mask) +, temperature_noise(config.seed ^ config.temperature.seed_mask) +, richness_noise(config.seed ^ config.richness.seed_mask) +, random_noise(config.seed ^ config.randomness.seed_mask) { } +void Generator::Scan() { + size_t count = 0; + for (size_t i = 0, end = types.Size(); i < end; ++i) { + if (types[i].generate) ++count; + } + candidates.reserve(count); +} void Generator::operator ()(Chunk &chunk) const noexcept { Chunk::Pos pos(chunk.Position()); @@ -28,26 +49,65 @@ void Generator::operator ()(Chunk &chunk) const noexcept { for (int y = 0; y < Chunk::height; ++y) { for (int x = 0; x < Chunk::width; ++x) { Block::Pos block_pos(x, y, z); - glm::vec3 gen_pos = (coords + block_pos) * stretch; - float val = OctaveNoise(solidNoise, coords + block_pos, 3, 0.5f, stretch, 2.0f); - if (val > solid_threshold) { - int type_val = int((typeNoise(gen_pos) + 1.0f) * solids.size()) % solids.size(); - chunk.SetBlock(block_pos, Block(solids[type_val])); - } else { - chunk.SetBlock(block_pos, Block(space)); - } + chunk.SetBlock(block_pos, Generate(coords + block_pos)); } } } - unsigned int random = 263167 * pos.x + 2097593 * pos.y + 426389 * pos.z; - for (int index = 0; index < Chunk::size; ++index) { - if (chunk.IsSurface(index)) { - random = random * 666649 + 7778777; - if ((random % 32) == 0) { - chunk.SetBlock(index, Block(light)); - } +} + +Block Generator::Generate(const glm::vec3 &pos) const noexcept { + float solidity = GetValue(solidity_noise, pos, config.solidity); + float humidity = GetValue(humidity_noise, pos, config.humidity); + float temperature = GetValue(temperature_noise, pos, config.temperature); + float richness = GetValue(richness_noise, pos, config.richness); + + candidates.clear(); + float total = 0.0f; + for (size_t i = 0, end = types.Size(); i < end; ++i) { + const BlockType &type = types[i]; + if (!type.generate) continue; + if (solidity < type.min_solidity || solidity > type.max_solidity) continue; + if (humidity < type.min_humidity || humidity > type.max_humidity) continue; + if (temperature < type.min_temperature || temperature > type.max_temperature) continue; + if (richness < type.min_richness || richness > type.max_richness) continue; + float solidity_match = 4.0f - ((solidity - type.mid_solidity) * (solidity - type.mid_solidity)); + float humidity_match = 4.0f - ((humidity - type.mid_humidity) * (humidity - type.mid_humidity)); + float temperature_match = 4.0f - ((temperature - type.mid_temperature) * (temperature - type.mid_temperature)); + float richness_match = 4.0f - ((richness - type.mid_richness) * (richness - type.mid_richness)); + float chance = (solidity_match + humidity_match + temperature_match + richness_match) * type.commonness; + total += chance; + candidates.emplace_back(&type, total); + } + if (candidates.empty()) { + return Block(0); + } + float random = GetValue(random_noise, pos, config.randomness); + if (random < 0.0f) random += 1.0f; + float value = random * total; + // TODO: change to binary search + for (const Candidate &cand : candidates) { + if (value < cand.threshold) { + return Block(cand.type->id); } } + // theoretically, this should never happen + return Block(candidates.back().type->id); +} + +float Generator::GetValue( + const SimplexNoise &noise, + const glm::vec3 &pos, + const Config::NoiseParam &conf +) noexcept { + return OctaveNoise( + noise, + pos, + conf.octaves, + conf.persistence, + conf.frequency, + conf.amplitude, + conf.growth + ); } } diff --git a/src/world/Generator.hpp b/src/world/Generator.hpp index aadf2cf..5251ac6 100644 --- a/src/world/Generator.hpp +++ b/src/world/Generator.hpp @@ -1,16 +1,17 @@ #ifndef BLANK_WORLD_GENERATOR_HPP_ #define BLANK_WORLD_GENERATOR_HPP_ -#include "Block.hpp" #include "../rand/SimplexNoise.hpp" #include "../rand/WorleyNoise.hpp" #include -#include +#include namespace blank { +class Block; +class BlockTypeRegistry; class Chunk; class Generator { @@ -18,28 +19,43 @@ class Generator { public: struct Config { std::uint64_t seed = 0; - float stretch = 64.0f; - float solid_threshold = 0.5f; + struct NoiseParam { + std::uint64_t seed_mask; + int octaves; + float persistence; + float frequency; + float amplitude; + float growth; + }; + NoiseParam solidity = { 0xA85033F6BCBDD110, 3, 0.5f, 1.0f/64.0f, 2.0f, 2.0f }; + NoiseParam humidity = { 0x3A463FB24B04A901, 3, 0.5f, 1.0f/256.0f, 2.0f, 2.0f }; + NoiseParam temperature = { 0x2530BA6C6134A9FB, 3, 0.5f, 1.0f/512.0f, 2.0f, 2.0f }; + NoiseParam richness = { 0x95A179F180103446, 3, 0.5f, 1.0f/128.0f, 2.0f, 2.0f }; + NoiseParam randomness = { 0x074453EEE1496390, 3, 0.5f, 1.0f/16.0f, 2.0f, 2.0f }; }; - explicit Generator(const Config &) noexcept; + explicit Generator(const Config &, const BlockTypeRegistry &) noexcept; - void operator ()(Chunk &) const noexcept; + // scan types for generation + void Scan(); - void Space(Block::Type t) noexcept { space = t; } - void Light(Block::Type t) noexcept { light = t; } - void Solids(const std::vector &s) { solids = s; } + void operator ()(Chunk &) const noexcept; private: - SimplexNoise solidNoise; - WorleyNoise typeNoise; + Block Generate(const glm::vec3 &position) const noexcept; + static float GetValue( + const SimplexNoise &, + const glm::vec3 &, + const Config::NoiseParam &) noexcept; - float stretch; - float solid_threshold; - - Block::Type space; - Block::Type light; - std::vector solids; +private: + const Config &config; + const BlockTypeRegistry &types; + SimplexNoise solidity_noise; + SimplexNoise humidity_noise; + SimplexNoise temperature_noise; + SimplexNoise richness_noise; + SimplexNoise random_noise; }; diff --git a/src/world/block.cpp b/src/world/block.cpp index 09acfef..8d49a64 100644 --- a/src/world/block.cpp +++ b/src/world/block.cpp @@ -81,6 +81,20 @@ BlockType::BlockType(bool v, const glm::vec3 &col, const Shape *s) noexcept , block_light(false) , collision(false) , collide_block(false) +, generate(false) +, min_solidity(0.5f) +, mid_solidity(0.75f) +, max_solidity(1.0f) +, min_humidity(-1.0f) +, mid_humidity(0.0f) +, max_humidity(1.0f) +, min_temperature(-1.0f) +, mid_temperature(0.0f) +, max_temperature(1.0f) +, min_richness(-1.0f) +, mid_richness(0.0f) +, max_richness(1.0f) +, commonness(1.0f) , fill({ false, false, false, false, false, false }) { }