]> git.localhorst.tv Git - blank.git/commitdiff
more parameters for world generation
authorDaniel Karbach <daniel.karbach@localhorst.tv>
Sat, 3 Oct 2015 17:00:24 +0000 (19:00 +0200)
committerDaniel Karbach <daniel.karbach@localhorst.tv>
Sat, 3 Oct 2015 17:00:24 +0000 (19:00 +0200)
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

assets
src/app/app.cpp
src/server/ServerState.cpp
src/standalone/MasterState.cpp
src/world/BlockType.hpp
src/world/Generator.cpp
src/world/Generator.hpp
src/world/block.cpp

diff --git a/assets b/assets
index b965c835cce06d762c9741f42dee34809bbf6a08..fc0d11c95840498d7588908bc882941a4b7eaa2a 160000 (submodule)
--- a/assets
+++ b/assets
@@ -1 +1 @@
-Subproject commit b965c835cce06d762c9741f42dee34809bbf6a08
+Subproject commit fc0d11c95840498d7588908bc882941a4b7eaa2a
index 4d2d2c098afa17bcde96083ddf384aeddacaa86b..98c7286a7970916c70a7b107b90b5e0b301f08de 100644 (file)
@@ -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") {
index 9020aa09473bdd3ec9f094e8fcab1578de4d9168..8c3f7e6196f92c5272cda3b61aa9128b825891c9 100644 (file)
@@ -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]);
index dccc0f958410764b1416c89ff6ac2dce6dbcd59c..6ead29980bf2cdc9bd10573a59c7593111f6ba38 100644 (file)
@@ -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();
index bdc4721e96dea3b4762cc219190b4d0c49379ff6..0cae9c00389767c013880e559f3a37bf1126c7c8 100644 (file)
@@ -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 {
index c37c18b6c2aa4a7f337286d6810bfd154ea6e002..f15a25245dc0d89413671fdcc3a4407c09e413b5 100644 (file)
@@ -1,5 +1,7 @@
 #include "Generator.hpp"
 
+#include "BlockType.hpp"
+#include "BlockTypeRegistry.hpp"
 #include "Chunk.hpp"
 #include "../rand/OctaveNoise.hpp"
 
 
 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<Candidate> 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
+       );
 }
 
 }
index aadf2cfbeed4aaa2f0167f10e524d863c997ecb2..5251ac69aed002c3a97e5b488e1ff863ddeba07c 100644 (file)
@@ -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 <cstdint>
-#include <vector>
+#include <glm/glm.hpp>
 
 
 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<Block::Type> &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<Block::Type> solids;
+private:
+       const Config &config;
+       const BlockTypeRegistry &types;
+       SimplexNoise solidity_noise;
+       SimplexNoise humidity_noise;
+       SimplexNoise temperature_noise;
+       SimplexNoise richness_noise;
+       SimplexNoise random_noise;
 
 };
 
index 09acfef9efcc01a2504c63459736fec9df5bd056..8d49a645f8e2b24d0a7c1dc4fe30ae6a5f347e8d 100644 (file)
@@ -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 }) {
 
 }