Biome setup & rudimentary generator

This commit is contained in:
KdotJPG 2020-03-01 18:13:39 -05:00 committed by Quentin Bazin
parent 53078388b4
commit 53bb1c14c0
12 changed files with 397 additions and 15 deletions

View File

@ -86,6 +86,10 @@ void Registry::serialize(sf::Packet &packet) const {
packet << u8((it->type() == "craft") ? DataType::CraftingRecipe : DataType::SmeltingRecipe)
<< *it;
}
for (auto &it : m_biomes) {
packet << u8(DataType::Biome) << *it;
}
}
void Registry::deserialize(sf::Packet &packet) {
@ -104,6 +108,9 @@ void Registry::deserialize(sf::Packet &packet) {
else if (type == u8(DataType::SmeltingRecipe)) {
registerRecipe<SmeltingRecipe>()->deserialize(packet);
}
else if (type == u8(DataType::Biome)) {
registerSerializedBiome<Biome>(packet);
}
}
}

View File

@ -35,6 +35,7 @@
#include "Item.hpp"
#include "Network.hpp"
#include "Recipe.hpp"
#include "Biome.hpp"
class Registry : public ISerializable {
public:
@ -66,6 +67,25 @@ class Registry : public ISerializable {
return m_recipes.back().get();
}
template<typename T>
auto registerBiome(const std::string &stringID, const std::string &label) -> typename std::enable_if<std::is_base_of<Biome, T>::value, T&>::type {
size_t id = m_biomes.size();
m_biomesID.emplace(stringID, id);
m_biomes.emplace_back(std::make_unique<T>(id, stringID, label));
return *static_cast<T*>(m_biomes.back().get());
}
template<typename T>
auto registerSerializedBiome(sf::Packet &packet) -> typename std::enable_if<std::is_base_of<Biome, T>::value, T&>::type {
m_biomes.emplace_back(std::make_unique<T>());
m_biomes.back()->deserialize(packet);
size_t id = m_biomes.size() - 1;
m_biomesID.emplace(m_biomes.back()->stringID(), id);
return *static_cast<T*>(m_biomes.back().get());
}
const Block &getBlock(std::size_t id) const { return *m_blocks.at(id).get(); }
const Item &getItem(std::size_t id) const { return m_items.at(id); }
@ -74,11 +94,14 @@ class Registry : public ISerializable {
const Recipe *getRecipe(const Inventory &inventory) const;
const Biome &getBiome(std::size_t id) const { return *m_biomes.at(id).get(); }
void serialize(sf::Packet &packet) const override;
void deserialize(sf::Packet &packet) override;
const std::vector<std::unique_ptr<Block>> &blocks() const { return m_blocks; }
const std::vector<Item> &items() const { return m_items; }
const std::vector<std::unique_ptr<Biome>> &biomes() const { return m_biomes; }
static Registry &getInstance() { return *s_instance; }
static void setInstance(Registry &instance) { s_instance = &instance; }
@ -89,15 +112,18 @@ class Registry : public ISerializable {
std::vector<std::unique_ptr<Block>> m_blocks;
std::vector<Item> m_items;
std::vector<std::unique_ptr<Recipe>> m_recipes;
std::vector<std::unique_ptr<Biome>> m_biomes;
std::unordered_map<std::string, u32> m_blocksID;
std::unordered_map<std::string, u32> m_itemsID;
std::unordered_map<std::string, u32> m_biomesID;
enum class DataType {
Block,
Item,
CraftingRecipe,
SmeltingRecipe,
Biome
};
};

View File

@ -0,0 +1,43 @@
/*
* =====================================================================================
*
* OpenMiner
*
* Copyright (C) 2018-2020 Unarelith, Quentin Bazin <openminer@unarelith.net>
* Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md)
*
* This file is part of OpenMiner.
*
* OpenMiner is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* OpenMiner is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with OpenMiner; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* =====================================================================================
*/
#include "Biome.hpp"
#include <SFML/Network/Packet.hpp>
#include "NetworkUtils.hpp"
Biome::Biome(u32 id, const std::string &stringID, const std::string &label) {
m_id = id;
std::string m_stringID;
std::string m_label;
}
void Biome::serialize(sf::Packet &packet) const {
packet << u32(m_id) << m_stringID << m_label << m_params << m_topBlock << m_groundBlock << m_beachBlock << m_liquidBlock;
}
void Biome::deserialize(sf::Packet &packet) {
packet >> m_id >> m_stringID >> m_label >> m_params >> m_topBlock >> m_groundBlock >> m_beachBlock >> m_liquidBlock;
}

View File

@ -0,0 +1,74 @@
/*
* =====================================================================================
*
* OpenMiner
*
* Copyright (C) 2018-2020 Unarelith, Quentin Bazin <openminer@unarelith.net>
* Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md)
*
* This file is part of OpenMiner.
*
* OpenMiner is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* OpenMiner is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with OpenMiner; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* =====================================================================================
*/
#ifndef BIOME_HPP_
#define BIOME_HPP_
#include <string>
#include <vector>
#include <gk/core/IntTypes.hpp>
#include "ISerializable.hpp"
class Biome : public ISerializable {
public:
Biome() = default;
Biome(u32 id, const std::string &stringID, const std::string &label);
~Biome() = default;
void serialize(sf::Packet &packet) const override;
void deserialize(sf::Packet &packet) override;
const std::string &stringID() const { return m_stringID; }
const std::string &label() const { return m_label; }
void setLabel(const std::string &label) { m_label = label; }
std::vector<double> getParams() { return m_params; }
const u16 getTopBlock() const { return m_topBlock; }
const u16 getGroundBlock() const { return m_groundBlock; }
const u16 getBeachBlock() const { return m_beachBlock; }
const u16 getLiquidBlock() const { return m_liquidBlock; }
void setParams(std::vector<double> value) { m_params = value; }
void setTopBlock(u16 value) { m_topBlock = value; }
void setGroundBlock(u16 value) { m_groundBlock = value; }
void setBeachBlock(u16 value) { m_beachBlock = value; }
void setLiquidBlock(u16 value) { m_liquidBlock = value; }
private:
u32 m_id;
std::string m_stringID;
std::string m_label;
// TODO something to distinguish the worldtype of biome
std::vector<double> m_params;
u16 m_topBlock;
u16 m_groundBlock;
u16 m_beachBlock;
u16 m_liquidBlock;
};
#endif // BIOME_HPP_

88
mods/default/biomes.lua Normal file
View File

@ -0,0 +1,88 @@
--
-- =====================================================================================
--
-- OpenMiner
--
-- Copyright (C) 2018-2020 Unarelith, Quentin Bazin <openminer@unarelith.net>
-- Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md)
--
-- This file is part of OpenMiner.
--
-- OpenMiner is free software; you can redistribute it and/or
-- modify it under the terms of the GNU Lesser General Public
-- License as published by the Free Software Foundation; either
-- version 2.1 of the License, or (at your option) any later version.
--
-- OpenMiner is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-- Lesser General Public License for more details.
--
-- You should have received a copy of the GNU Lesser General Public License
-- along with OpenMiner; if not, write to the Free Software Foundation, Inc.,
-- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
--
-- =====================================================================================
--
mod:biome {
id = "default:grassland",
name = "Grassland",
params = {
temperature = -0.6,
precipitation = 0.4
},
top_block = "default:grass",
ground_block = "default:dirt",
beach_block = "default:sand",
liquid_block = "default:water",
flora = {
{
block = "default:tallgrass",
spawns_on = "default:grass",
probability = 0.25
},
{
block = "default:flower",
spawns_on = "default:grass",
probability = 0.1
}
}
}
mod:biome {
id = "default:mountain",
name = "Mountain",
params = {
temperature = 0.5,
precipitation = 0.3
},
top_block = "default:stone",
ground_block = "default:stone",
beach_block = "default:stone",
liquid_block = "default:water",
flora = { }
}
mod:biome {
id = "default:barren",
name = "Barren",
params = {
temperature = 0.3,
precipitation = -0.7
},
top_block = "default:dirt",
ground_block = "default:dirt",
beach_block = "default:dirt",
liquid_block = "default:water",
flora = { }
}

View File

@ -29,6 +29,7 @@ mod = LuaMod.new("default")
dofile("mods/default/blocks.lua")
dofile("mods/default/items.lua")
dofile("mods/default/recipes.lua")
dofile("mods/default/biomes.lua")
openminer:world():terrain_generator():set_blocks({
dirt = "default:dirt",

View File

@ -30,6 +30,7 @@
#include "LuaMod.hpp"
#include "Registry.hpp"
#include "ServerBlock.hpp"
#include "Biome.hpp"
#include "SmeltingRecipe.hpp"
void LuaMod::registerBlock(const sol::table &table) {
@ -142,11 +143,32 @@ void LuaMod::registerSmeltingRecipe(const sol::table &table) {
Registry::getInstance().registerRecipe<SmeltingRecipe>(input, output);
}
void LuaMod::registerBiome(const sol::table &table) {
std::string stringID = m_id + ":" + table["id"].get<std::string>();
std::string label = table["name"].get<std::string>();
Biome &biome = Registry::getInstance().registerBiome<Biome>(stringID, label);
// TODO eventually a WorldType could have a list of biome parameter names in order, and we could use those as the ordered keys.
// Currently hardcoding "temperature" and "precipitation" to get something functional.
size_t nBiomeParams = 2;
std::vector<double> params(nBiomeParams);
params[0] = table["params"]["temperature"];
params[1] = table["params"]["precipitation"];
biome.setParams(params);
biome.setTopBlock(Registry::getInstance().getBlockFromStringID(table["top_block"]).id());
biome.setGroundBlock(Registry::getInstance().getBlockFromStringID(table["ground_block"]).id());
biome.setBeachBlock(Registry::getInstance().getBlockFromStringID(table["beach_block"]).id());
biome.setLiquidBlock(Registry::getInstance().getBlockFromStringID(table["liquid_block"]).id());
}
void LuaMod::initUsertype(sol::state &lua) {
lua.new_usertype<LuaMod>("LuaMod",
sol::constructors<LuaMod(std::string)>(),
"id", &LuaMod::id,
"block", &LuaMod::registerBlock,
"biome", &LuaMod::registerBiome,
"item", &LuaMod::registerItem,
"crafting_recipe", &LuaMod::registerCraftingRecipe,
"smelting_recipe", &LuaMod::registerSmeltingRecipe

View File

@ -38,6 +38,7 @@ class LuaMod {
void registerItem(const sol::table &table);
void registerCraftingRecipe(const sol::table &table);
void registerSmeltingRecipe(const sol::table &table);
void registerBiome(const sol::table &table);
const std::string &id() const { return m_id; }

View File

@ -0,0 +1,78 @@
/*
* =====================================================================================
*
* OpenMiner
*
* Copyright (C) 2018-2020 Unarelith, Quentin Bazin <openminer@unarelith.net>
* Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md)
*
* This file is part of OpenMiner.
*
* OpenMiner is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* OpenMiner is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with OpenMiner; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* =====================================================================================
*/
#include "TerrainBiomeSampler.hpp"
#include "Registry.hpp"
#include "Biome.hpp"
TerrainBiomeSampler::TerrainBiomeSampler() {
paramNoisesPtr = new FastNoise[nBiomeParams];
for (u8 i = 0; i < nBiomeParams; i++) {
paramNoisesPtr[i].SetNoiseType(FastNoise::NoiseType::SimplexFractal);
paramNoisesPtr[i].SetFrequency(1 / 800.0f);
paramNoisesPtr[i].SetFractalOctaves(5);
paramNoisesPtr[i].SetSeed(i);
}
}
TerrainBiomeSampler::~TerrainBiomeSampler() {
delete paramNoisesPtr;
}
u16 TerrainBiomeSampler::getBiomeIndexAt(s32 x, s32 y) const {
// Compute noise instances
double* biomeParamsPtr = new double[nBiomeParams];
for (u8 i = 0; i < nBiomeParams; i++) {
biomeParamsPtr[i] = paramNoisesPtr[i].GetNoise(x, y);
}
// TODO with a lot of biomes, perhaps we want an R-Tree or similar, instead of a long loop.
// Should also finish solving for analytic blending, or find completely separate solution such as isotropically-modified genlayer
// If we continue with temp/precip/etc params, need to write a weighted lloyd smoother so biomes becone fairly represented.
// True temp/precip values can then be re-interpolated out from the Voronoi diagram using a neighborhood figure "kernel".
// TODO with multiple worldtypes added, need to only consider biomes in one worldtype.
u16 decidedBiomeIndex = 0;
double decidedBiomeDeviation = 0xFFFF;
u16 j = 0;
for (auto &biome : Registry::getInstance().biomes()) {
double deviation = 0;
for (int i = 0; i < nBiomeParams; i++) {
double dp = biomeParamsPtr[i] - biome.get()->getParams()[i];
deviation += dp * dp;
}
if (deviation < decidedBiomeDeviation) {
decidedBiomeDeviation = deviation;
decidedBiomeIndex = j;
}
j++;
}
delete biomeParamsPtr;
return decidedBiomeIndex;
}

View File

@ -0,0 +1,47 @@
/*
* =====================================================================================
*
* OpenMiner
*
* Copyright (C) 2018-2020 Unarelith, Quentin Bazin <openminer@unarelith.net>
* Copyright (C) 2019-2020 the OpenMiner contributors (see CONTRIBUTORS.md)
*
* This file is part of OpenMiner.
*
* OpenMiner is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* OpenMiner is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with OpenMiner; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* =====================================================================================
*/
#ifndef TERRAINBIOMESAMPLER_HPP_
#define TERRAINBIOMESAMPLER_HPP_
#include <gk/core/IntTypes.hpp>
#include "FastNoise.hpp"
#include "Biome.hpp"
class TerrainBiomeSampler {
public:
TerrainBiomeSampler(); // TODO should eventually take a worldtype
~TerrainBiomeSampler();
u16 getBiomeIndexAt(s32 x, s32 y) const;
//std::vector<WeightedIndex> getWeightedBiomeIndicesAt(double x, double y);
private:
u8 nBiomeParams = 2; // TODO should be defined in the worldtype
FastNoise* paramNoisesPtr;
};
#endif // TERRAINBIOMESAMPLER_HPP_

View File

@ -63,6 +63,10 @@ void TerrainGenerator::fastNoiseGeneration(ServerChunk &chunk) const {
Chunk *topChunk = chunk.getSurroundingChunk(Chunk::Top);
for(int y = 0 ; y < CHUNK_DEPTH ; y++) {
for(int x = 0 ; x < CHUNK_WIDTH ; x++) {
u16 biomeIndex = biomeSampler.getBiomeIndexAt(x + chunk.x() * CHUNK_WIDTH, y + chunk.y() * CHUNK_DEPTH);
auto &biome = Registry::getInstance().getBiome(biomeIndex);
// Land height
double n = noise.GetNoise(-x - chunk.x() * CHUNK_WIDTH, y + chunk.y() * CHUNK_DEPTH);
double h = 10 + n * 20;
@ -115,27 +119,16 @@ void TerrainGenerator::fastNoiseGeneration(ServerChunk &chunk) const {
}
else {
if (z + chunk.z() * CHUNK_HEIGHT >= h - 1 && z + chunk.z() * CHUNK_HEIGHT > SEALEVEL - 1)
chunk.setBlockRaw(x, y, z, m_grassBlockID);
chunk.setBlockRaw(x, y, z, biome.getTopBlock());
else if (z + chunk.z() * CHUNK_HEIGHT <= SEALEVEL - 1 && h < SEALEVEL && z + chunk.z() * CHUNK_HEIGHT > h - 3)
chunk.setBlockRaw(x, y, z, m_sandBlockID);
chunk.setBlockRaw(x, y, z, biome.getBeachBlock());
else if (z + chunk.z() * CHUNK_HEIGHT > h - 3)
chunk.setBlockRaw(x, y, z, m_dirtBlockID);
chunk.setBlockRaw(x, y, z, biome.getGroundBlock());
else
chunk.setBlockRaw(x, y, z, m_stoneBlockID);
if ((rand() % 4096) == 0)
oreFloodFill(chunk, x, y, z, m_stoneBlockID, m_ironOreBlockID, 2);
// Caves
float n2 = noise2d(-(x + chunk.x() * CHUNK_WIDTH) / 256.0, (y + chunk.y() * CHUNK_DEPTH) / 256.0, 8, 0.3) * 4;
float r2 = noise3d_abs(-(x + chunk.x() * CHUNK_WIDTH) / 512.0f, (z + chunk.z() * CHUNK_HEIGHT) / 512.0f, (y + chunk.y() * CHUNK_DEPTH) / 512.0f, 4, 0.1);
float r3 = noise3d_abs(-(x + chunk.x() * CHUNK_WIDTH) / 512.0f, (z + chunk.z() * CHUNK_HEIGHT) / 128.0f, (y + chunk.y() * CHUNK_DEPTH) / 512.0f, 4, 1);
float r4 = n2 * 5 + r2 * r3 * 20;
if (r4 > 6 && r4 < 8 && h > SEALEVEL) {
chunk.setBlockRaw(x, y, z - 1, 0);
chunk.setBlockRaw(x, y, z, 0);
chunk.setBlockRaw(x, y, z + 1, 0);
}
}
if (topChunk && topChunk->isInitialized()) {

View File

@ -28,8 +28,8 @@
#define TERRAINGENERATOR_HPP_
#include <gk/core/IntTypes.hpp>
#include <sol.hpp>
#include "TerrainBiomeSampler.hpp"
class ServerChunk;
@ -48,6 +48,8 @@ class TerrainGenerator {
static float noise2d(double x, double y, int octaves, float persistence);
static float noise3d_abs(double x, double y, double z, int octaves, float persistence);
TerrainBiomeSampler biomeSampler;
u16 m_dirtBlockID = 0;
u16 m_grassBlockID = 0;
u16 m_stoneBlockID = 0;