Documentation on the MapGen class, fix hint in setBlock.

master
Auri 2020-11-09 11:27:22 -08:00
parent e49ab18617
commit 2c97101c39
3 changed files with 197 additions and 74 deletions

View File

@ -1,13 +1,13 @@
<component name="ProjectCodeStyleConfiguration">
<code_scheme name="Project" version="173">
<Objective-C>
<option name="INDENT_VISIBILITY_KEYWORDS" value="4" />
<option name="INDENT_PREPROCESSOR_DIRECTIVE" value="4" />
<option name="KEEP_CASE_EXPRESSIONS_IN_ONE_LINE" value="true" />
<option name="FUNCTION_PARAMETERS_ALIGN_MULTILINE" value="false" />
<option name="FUNCTION_CALL_ARGUMENTS_ALIGN_MULTILINE" value="false" />
<option name="CLASS_CONSTRUCTOR_INIT_LIST_ALIGN_MULTILINE" value="false" />
<option name="ALIGN_INIT_LIST_IN_COLUMNS" value="false" />
<option name="SPACE_BEFORE_INIT_LIST" value="true" />
<option name="SPACE_BEFORE_POINTER_IN_DECLARATION" value="false" />
<option name="SPACE_AFTER_POINTER_IN_DECLARATION" value="true" />
<option name="SPACE_BEFORE_REFERENCE_IN_DECLARATION" value="false" />
@ -37,10 +37,11 @@
</rules>
</Objective-C-extensions>
<codeStyleSettings language="ObjectiveC">
<option name="BLANK_LINES_AFTER_CLASS_HEADER" value="1" />
<option name="BLANK_LINES_AROUND_CLASS" value="0" />
<option name="ELSE_ON_NEW_LINE" value="true" />
<option name="CATCH_ON_NEW_LINE" value="true" />
<option name="INDENT_CASE_FROM_SWITCH" value="false" />
<option name="ALIGN_MULTILINE_TERNARY_OPERATION" value="false" />
<option name="ALIGN_MULTILINE_ARRAY_INITIALIZER_EXPRESSION" value="false" />
<option name="SPACE_WITHIN_ARRAY_INITIALIZER_BRACES" value="true" />
<indentOptions>

View File

@ -6,57 +6,31 @@
#include "MapGen.h"
#include "game/def/BiomeDef.h"
#include "game/atlas/BiomeAtlas.h"
#include "game/Subgame.h"
#include "MapGenProps.h"
#include "game/def/BlockDef.h"
#include "game/atlas/DefinitionAtlas.h"
#include "world/dim/Dimension.h"
#include "world/dim/chunk/Chunk.h"
#include "world/World.h"
#include "game/Subgame.h"
#include "util/Schematic.h"
#include "game/def/BiomeDef.h"
#include "game/def/BlockDef.h"
#include "world/dim/Dimension.h"
#include "game/atlas/BiomeAtlas.h"
#include "world/dim/chunk/Chunk.h"
#include "game/atlas/DefinitionAtlas.h"
MapGen::MapGen(Subgame& game, World& world, unsigned int seed, std::unordered_set<std::string> biomes) :
game(game), world(world), props(seed) {
std::unordered_set<unsigned int> biomeIndices{};
std::unordered_set<unsigned int> biomeIndices {};
for (const auto& str : biomes) {
if (str[0] == '#') {
for (auto& biome : game.getBiomes().biomesFromTag(str.substr(1, str.length() - 1))) {
if (str[0] == '#')
for (auto& biome : game.getBiomes().biomesFromTag(str.substr(1, str.length() - 1)))
biomeIndices.insert(biome->index);
}
}
else biomeIndices.insert(game.getBiomes().biomeFromStr(str).index);
}
generateVoronoi(biomeIndices);
}
void MapGen::generateVoronoi(const std::unordered_set<unsigned int>& biomes) {
std::vector<std::pair<glm::vec3, unsigned short>> points{};
for (auto biomeInd : biomes) {
auto& biome = game.getBiomes().biomeFromId(biomeInd);
points.emplace_back(glm::vec3{
static_cast<unsigned short>(std::fmin(voronoiSize - 1,
std::fmax(0, (biome.temperature + 1) / 2 * voronoiSize))),
static_cast<unsigned short>(std::fmin(voronoiSize - 1, std::fmax(0, biome.humidity * voronoiSize))),
static_cast<unsigned short>(std::fmin(voronoiSize - 1, std::fmax(0, biome.roughness * voronoiSize)))
}, biomeInd);
}
voronoi.setPoints(points);
}
unsigned int MapGen::getBiomeAt(float temperature, float humidity, float roughness) {
return voronoi.getPoint(
static_cast<unsigned short>(std::fmin(voronoiSize - 1, std::fmax(0, (temperature + 1) / 2 * voronoiSize))),
static_cast<unsigned short>(std::fmin(voronoiSize - 1, std::fmax(0, humidity * voronoiSize))),
static_cast<unsigned short>(std::fmin(voronoiSize - 1, std::fmax(0, roughness * voronoiSize))));
}
std::unique_ptr<MapGen::CreatedSet> MapGen::generateChunk(unsigned int dim, glm::ivec3 pos) {
[[maybe_unused]] std::unique_ptr<MapGen::CreatedSet> MapGen::generateChunk(unsigned int dim, glm::ivec3 pos) {
return generateArea(dim, pos, 1);
}
@ -82,7 +56,7 @@ std::unique_ptr<MapGen::CreatedSet> MapGen::generateArea(unsigned int dim, glm::
// Generate Biome Topmap
std::vector<unsigned int> biomeMap{};
std::vector<unsigned int> biomeMap {};
biomeMap.resize((job.size * 16 + 1) * (job.size * 16 + 1));
for (unsigned short i = 0; i < biomeMap.size(); i++) {
@ -111,7 +85,7 @@ std::unique_ptr<MapGen::CreatedSet> MapGen::generateArea(unsigned int dim, glm::
// Generate Chunks
glm::ivec3 pos{};
glm::ivec3 pos {};
for (pos.x = 0; pos.x < job.size; pos.x++)
for (pos.z = 0; pos.z < job.size; pos.z++) {
std::unique_ptr<ChunkData> densityAbove = nullptr;
@ -122,7 +96,7 @@ std::unique_ptr<MapGen::CreatedSet> MapGen::generateArea(unsigned int dim, glm::
}
std::unique_ptr<ChunkData> density = populateChunkDensity(job, pos);
std::unique_ptr<ChunkData> depth = populateChunkDepth(job, density, std::move(densityAbove));
std::unique_ptr<ChunkData> depth = populateChunkDepth(density, std::move(densityAbove));
generateChunkBlocks(job, pos, biomeMap, *depth);
generateChunkStructures(job, pos, biomeMap, *depth);
@ -142,6 +116,29 @@ std::unique_ptr<MapGen::CreatedSet> MapGen::generateArea(unsigned int dim, glm::
return std::move(created);
}
void MapGen::generateVoronoi(const std::unordered_set<unsigned int>& biomes) {
std::vector<std::pair<glm::vec3, unsigned short>> points {};
for (auto biomeInd : biomes) {
auto& biome = game.getBiomes().biomeFromId(biomeInd);
points.emplace_back(glm::vec3 {
static_cast<unsigned short>(std::fmin(voronoiSize - 1,
std::fmax(0, (biome.temperature + 1) / 2 * voronoiSize))),
static_cast<unsigned short>(std::fmin(voronoiSize - 1, std::fmax(0, biome.humidity * voronoiSize))),
static_cast<unsigned short>(std::fmin(voronoiSize - 1, std::fmax(0, biome.roughness * voronoiSize)))
}, biomeInd);
}
voronoi.setPoints(points);
}
unsigned int MapGen::getBiomeAt(float temperature, float humidity, float roughness) {
return voronoi.getPoint(
static_cast<unsigned short>(std::fmin(voronoiSize - 1, std::fmax(0, (temperature + 1) / 2 * voronoiSize))),
static_cast<unsigned short>(std::fmin(voronoiSize - 1, std::fmax(0, humidity * voronoiSize))),
static_cast<unsigned short>(std::fmin(voronoiSize - 1, std::fmax(0, roughness * voronoiSize))));
}
std::unique_ptr<MapGen::ChunkData> MapGen::populateChunkDensity(MapGen::Job& job, glm::ivec3 localPos) {
auto data = std::make_unique<ChunkData>();
@ -155,8 +152,9 @@ std::unique_ptr<MapGen::ChunkData> MapGen::populateChunkDensity(MapGen::Job& job
return data;
}
std::unique_ptr<MapGen::ChunkData> MapGen::populateChunkDepth(Job& job, std::unique_ptr<ChunkData>& chunkDensity,
std::unique_ptr<MapGen::ChunkData> MapGen::populateChunkDepth(std::unique_ptr<ChunkData>& chunkDensity,
std::unique_ptr<ChunkData> chunkDensityAbove) {
auto data = std::make_unique<ChunkData>();
for (unsigned short i = 0; i < 256; i++) {
@ -215,12 +213,12 @@ MapGen::generateChunkBlocks(Job& job, glm::ivec3 localPos, std::vector<unsigned
}
float depth = depthMap[i];
unsigned int blockID
= partialBlock != DefinitionAtlas::INVALID ? partialBlock
: depth <= 1 ? DefinitionAtlas::AIR
: depth <= 2 ? biome.topBlock
: depth <= 4 ? biome.soilBlock
: biome.rockBlock;
unsigned int blockID =
partialBlock != DefinitionAtlas::INVALID ? partialBlock
: depth <= 1 ? DefinitionAtlas::AIR
: depth <= 2 ? biome.topBlock
: depth <= 4 ? biome.soilBlock
: biome.rockBlock;
if (biomeID != cBiomeID) {
chunk.biomes.emplace_back(i);
@ -241,6 +239,7 @@ MapGen::generateChunkBlocks(Job& job, glm::ivec3 localPos, std::vector<unsigned
void MapGen::generateChunkStructures(Job& job, glm::ivec3 localPos, std::vector<unsigned int> biomeMap,
ChunkData& depthMap) {
glm::vec3 posFloat = job.pos + localPos;
std::default_random_engine generator(posFloat.x + posFloat.y / M_PI + posFloat.z / (M_PI * 2));
std::uniform_real_distribution<float> distribution(0, 1);
@ -277,14 +276,16 @@ void MapGen::generateChunkStructures(Job& job, glm::ivec3 localPos, std::vector<
void MapGen::setBlock(MapGen::Job& job, glm::ivec3 worldPos, unsigned int block, std::shared_ptr<Chunk> hint) {
if (block == DefinitionAtlas::INVALID) return;
glm::ivec3 chunkPos = Space::Chunk::world::fromBlock(worldPos);
auto& chunk = *(*job.chunks->emplace(chunkPos, std::make_shared<Chunk>(chunkPos, true)).first).second;
// if (!job.chunks->count(chunkPos)) return;
// auto& chunk = *job.chunks->at(chunkPos);
unsigned int ind = Space::Block::index(worldPos);
if (chunk.getBlock(ind) <= DefinitionAtlas::AIR) chunk.setBlock(ind, block);
if (hint && Space::Chunk::world::fromBlock(worldPos) == hint->getPos()) {
if (hint->getBlock(ind) <= DefinitionAtlas::AIR) hint->setBlock(ind, block);
}
else {
glm::ivec3 chunkPos = Space::Chunk::world::fromBlock(worldPos);
auto chunk = *(*job.chunks->emplace(chunkPos, std::make_shared<Chunk>(chunkPos, true)).first).second;
if (chunk.getBlock(ind) <= DefinitionAtlas::AIR) chunk.setBlock(ind, block);
}
}
//void MapGen::generateSunlight(MapGen::chunk_partials_map &chunks, glm::ivec3 mbPos) {

View File

@ -17,33 +17,45 @@
#include "util/Voronoi3D.h"
class World;
class Chunk;
class Subgame;
class BiomeDef;
class BiomeAtlas;
class DefinitionAtlas;
class MapGen {
public:
constexpr static unsigned int BIOP = 4; // Biome Prop Precision
constexpr static unsigned int TERP = 4; // Terrain Map Precision
public:
/** The precision of the Biome map, as a divisor of the chunk size. */
constexpr static unsigned int BIOP = 4;
typedef std::unordered_map<glm::ivec3, std::shared_ptr<Chunk>, Vec::ivec3> ChunkMap;
/** The precision of the Terrain maps, as a divisor of the chunk size. */
constexpr static unsigned int TERP = 4;
/** A type alias for the returned list of chunk positions that were generated. */
typedef std::unordered_set<glm::ivec3, Vec::ivec3> CreatedSet;
/**
* A struct containing all the information for a generation job.
* Contains a list of chunks, Noise samples, and the world position of the the job's root.
*/
struct Job {
/** A type alias for the type the map of Chunks stored in the Job. */
typedef std::unordered_map<glm::ivec3, std::shared_ptr<Chunk>, Vec::ivec3> ChunkMap;
/**
* Creates a new job with the root position and size specified, and initializes the NoiseSample params.
* @param pos - The root position of the job.
* @param size - The size in chunks of the job.
*/
Job(glm::ivec3 pos, unsigned int size) :
pos(pos), size(size),
volume{{ size * TERP, (size + 1) * TERP }, { 1, 1.25 }}, heightmap{{ size * TERP, 0 }},
temperature{{ size * BIOP, 0 }}, roughness{{ size * BIOP, 0 }}, humidity{{ size * BIOP, 0 }} {}
volume {{ size * TERP, (size + 1) * TERP }, { 1, 1.25 }}, heightmap {{ size * TERP, 0 }},
temperature {{ size * BIOP, 0 }}, roughness {{ size * BIOP, 0 }}, humidity {{ size * BIOP, 0 }} {}
glm::ivec3 pos{};
unsigned int size{};
glm::ivec3 pos {};
unsigned int size {};
std::unique_ptr<ChunkMap> chunks = std::make_unique<ChunkMap>();
@ -55,29 +67,138 @@ class MapGen {
MapGen(const MapGen& o) = delete;
/**
* Create a MapGen object with the seed and biomes provided.
*
* @param game - A reference to the subgame, for block and biome definitions.
* @param world - A reference to the world, for assigning the blocks to.
* @param seed - A seed to base the generation off of.
* @param biomes - A list of biome identifiers or tags to include in generation.
*/
MapGen(Subgame& game, World& world, unsigned int seed, std::unordered_set<std::string> biomes);
std::unique_ptr<CreatedSet> generateChunk(unsigned int dim, glm::ivec3 pos);
/**
* Generate a single chunk at the dimension and position provided.
* As with all generate* functions, this may result in extraneous chunk partials being created.
* This is the least efficient generation call and should be used only in extenuating circumstances.
*
* @param dim - The dimension to insert the finished chunk into.
* @param pos - The position in the dimension to generate the chunk.
* @returns a set of positions that were generated by this function call.
*/
[[maybe_unused]] std::unique_ptr<CreatedSet> generateChunk(unsigned int dim, glm::ivec3 pos);
/**
* Generate a mapblock at the dimension and position provided.
* As with all generate* functions, this may result in extraneous chunk partials being created.
* Commonly used by the ServerGenStream for regular chunk generation.
*
* @param dim - The dimension to insert the finished chunk into.
* @param pos - The position in the dimension to generate the chunk.
* @returns a set of positions that were generated by this function call.
*/
std::unique_ptr<CreatedSet> generateMapBlock(unsigned int dim, glm::ivec3 pos);
std::unique_ptr<CreatedSet> generateArea(unsigned int dim, glm::ivec3 origin, unsigned int size = 1);
/**
* The underlying generate function called by both generateMapBlock and generateChunk.
* Can also be called on it's own to generate an arbitrary region of chunks.
* As with all generate* functions, this may result in extraneous chunk partials being created.
*
* @param dim - The dimension to insert the finished chunk into.
* @param pos - The position in the dimension to generate the chunk.
* @return - A set of positions that were generated by this function call.
*/
std::unique_ptr<CreatedSet> generateArea(unsigned int dim, glm::ivec3 origin, unsigned int size = 1);
private:
/**
* Get the closest biome to the provided environmental values from the Vonoroi map.
* Returns the index of the matched biome.
*
* @param temperature - The temperature value of the position to check.
* @param humidity - The humidity value of the position to check.
* @param roughness - The roughness value of the position to check.
* @returns the biome index of the environmentally closest biome.
*/
private:
unsigned int getBiomeAt(float temperature, float humidity, float roughness);
/**
* Generate the Vonoroi biome map, using the biomes listed,
* according to their definition parameters.
*
* @param biomes - The biomes to add to the map.
*/
void generateVoronoi(const std::unordered_set<unsigned int>& biomes);
/**
* Create a density array for a chunk using a generation Job and an offset within it.
* Returns a flattened array of block densities for every point in the chunk.
*
* @param job - The Job to pull the data from.
* @param localPos - The offset of the chunk's data within the job.
* @returns a ChunkData array containing the chunk's density.
*/
static std::unique_ptr<ChunkData> populateChunkDensity(Job& job, glm::ivec3 localPos);
static std::unique_ptr<ChunkData> populateChunkDepth(Job& job, std::unique_ptr<ChunkData>& chunkDensity,
/**
* Create a depth array for a chunk using a generation Job and the chunk densities around it.
* Returns a flattened array of block depths for every point in the chunk.
*
* @param chunkDensity - A density array of the current chunk.
* @param chunkDensityAbove - A density array of the chunk above it.
* @returns a ChunkData array containing the chunk's depth.
*/
static std::unique_ptr<ChunkData> populateChunkDepth(std::unique_ptr<ChunkData>& chunkDensity,
std::unique_ptr<ChunkData> chunkDensityAbove);
/**
* Generates a chunk's blocks from a generation Job and an offset within it, and inserts it into the Job.
* Combines with any partials that have been previously created at that position within job.
*
* Declares the chunk generated, and counts renderable blocks within it. Though later functions manipulate
* chunk data, they do so using the chunk setBlock method, making it safe to count at this stage.
*
* @param job - The Job to pull the data from.
* @param localPos - The offset of the chunk within the job.
* @param biomeMap - The two-dimensional biome array of the entire generation area.
* @param depthMap - The depth map of the chunk being generated.
*/
void generateChunkBlocks(Job& job, glm::ivec3 localPos, std::vector<unsigned int> biomeMap, ChunkData& depthMap);
/**
* Generates structures for a Chunk based on data within the generation job and an offset within it.
* May create chunk partials, inserting them back into the Job for later completion.
*
* @param job - The job to pull the data from.
* @param localPos - The offset of the chunk within the job.
* @param biomeMap - The two-dimensional biome array of the entire generation area.
* @param depthMap - The depth map of the chunk being generated.
*/
void
generateChunkStructures(Job& job, glm::ivec3 localPos, std::vector<unsigned int> biomeMap, ChunkData& depthMap);
/**
* Sets a block at the position specified into the Job, if the block at said position is not filled by
* a material greater than air. If a chunk does not exist at the specified position, a partial is generated
* into the chunk map, and the block is assigned on there.
*
* @param job - The job to pull the data from.
* @param worldPos - The world position to set the block at.
* @param block - The block to set.
* @param hint - An optional parameter that may speed up the function if set to the chunk to set to.
*/
static void setBlock(Job& job, glm::ivec3 worldPos, unsigned int block, std::shared_ptr<Chunk> hint);
// // Generate sunlight on the mapgen threads to speed up perf
@ -89,7 +210,7 @@ class MapGen {
unsigned int seed = 0;
constexpr const static unsigned short voronoiSize = 64;
Voronoi3D voronoi{ voronoiSize };
Voronoi3D voronoi { voronoiSize };
Subgame& game;
World& world;