Chunk Partials, functional tree generation, happy happy happy

master
Nicole Collings 2020-01-30 23:53:47 -08:00
parent dbcfe5c822
commit fb0fd13d4a
11 changed files with 171 additions and 122 deletions

View File

@ -76,8 +76,10 @@ void MapGen::generateChunk(chunk_partials_map& chunks, glm::ivec3 worldPos) {
buildDensityMap(chunk.first, worldPos);
buildElevationMap(chunks, chunk);
fillChunkBlocks(chunk);
fillChunkStructures(chunk);
generateBlocks(chunk);
generateStructures(chunks, chunk);
chunk.second->generated = true;
}
void MapGen::buildDensityMap(MapGenJob* job, glm::ivec3 worldPos) {
@ -115,6 +117,7 @@ void MapGen::buildDensityMap(MapGenJob* job, glm::ivec3 worldPos) {
void MapGen::buildElevationMap(chunk_partials_map& chunks, chunk_partial& chunk) {
glm::ivec3 worldPos = chunk.second->pos;
MapGenJob* upperJob = nullptr;
bool createdUpperJob = false;
@ -129,8 +132,6 @@ void MapGen::buildElevationMap(chunk_partials_map& chunks, chunk_partial& chunk)
glm::ivec3 rel = worldPos + glm::ivec3 {0, 1, 0};
if (chunks.count(rel) != 0) upperJob = chunks.at(rel).first;
else {
// TODO: Consider pushing this into the partials array? Could be used to save some mapgen time.. but could also complicate the system.
// If I do decide to do that I need to remove the delete command at the end of this function.
upperJob = new MapGenJob();
buildDensityMap(upperJob, rel);
createdUpperJob = true;
@ -156,10 +157,11 @@ void MapGen::buildElevationMap(chunk_partials_map& chunks, chunk_partial& chunk)
if (createdUpperJob) delete upperJob;
}
void MapGen::fillChunkBlocks(chunk_partial& chunk) {
void MapGen::generateBlocks(chunk_partial& chunk) {
glm::ivec3 lp;
for (unsigned short m = 0; m < 4096; m++) {
if (chunk.second->getBlock(m) != DefinitionAtlas::INVALID) continue;
Vec::indAssignVec(m, lp);
auto biome = biomes.getBiomeAt(chunk.first->temperature.get(lp), chunk.first->humidity.get(lp), chunk.first->roughness.get(lp));
@ -174,10 +176,10 @@ void MapGen::fillChunkBlocks(chunk_partial& chunk) {
: biome.rockBlock;
}
chunk.second->mgRegenEmpty();
chunk.second->calcNonAirBlocks();
}
void MapGen::fillChunkStructures(chunk_partial& chunk) {
void MapGen::generateStructures(chunk_partials_map& chunks, chunk_partial& chunk) {
unsigned int cWood = defs.blockFromStr("zeus:default:wood").index;
unsigned int cLeaves = defs.blockFromStr("zeus:default:leaves").index;
@ -189,22 +191,71 @@ void MapGen::fillChunkStructures(chunk_partial& chunk) {
glm::ivec3 p = wp * 16 + lp;
if (treeAbs.GetValue(p.x, p.y, p.z) > 1.2 && chunk.first->density[m] <= 2 && chunk.first->density[m] > 1) {
if (treeAbs.GetValue(p.x, p.y, p.z) > 1.2 && chunk.first->depth[m] <= 2 && chunk.first->depth[m] > 1) {
glm::ivec3 rp {};
for (unsigned int i = 0; i < 5; i++) {
rp.x = lp.x - 2 + i;
rp.x = p.x - 2 + i;
for (unsigned int j = 0; j < 5; j++) {
rp.z = lp.z - 2 + j;
rp.z = p.z - 2 + j;
for (unsigned int k = 0; k < 2; k++) {
rp.y = lp.y + 3 + k;
chunk.second->setBlock(rp, cLeaves);
rp.y = p.y + 3 + k;
setBlock(rp, cLeaves, chunks);
}
}
}
rp = {};
for (unsigned int i = 0; i < 3; i++) {
rp.x = p.x - 1 + i;
for (unsigned int j = 0; j < 3; j++) {
rp.z = p.z - 1 + j;
for (unsigned int k = 0; k < 2; k++) {
rp.y = p.y + 5 + k;
setBlock(rp, cLeaves, chunks);
}
}
}
for (unsigned int i = 0; i < 5; i++) {
chunk.second->setBlock(lp, cWood);
if (++lp.y > 15) break;
setBlock(p, cWood, chunks);
p.y++;
}
}
}
}
void MapGen::setBlock(glm::ivec3 worldPos, unsigned int block, MapGen::chunk_partials_map &chunks) {
glm::ivec3 chunkPos = Space::Chunk::world::fromBlock(worldPos);
BlockChunk* chunk = nullptr;
if (chunks.count(chunkPos)) chunk = chunks.at(chunkPos).second;
else {
chunk = new BlockChunk();
chunk->pos = chunkPos;
chunks.insert(std::pair<glm::ivec3, chunk_partial>{chunkPos, {new MapGenJob(), chunk}});
}
chunk->setBlock(Space::Block::relative::toChunk(worldPos), block);
}
std::shared_ptr<BlockChunk> MapGen::combinePartials(std::shared_ptr<BlockChunk> a, std::shared_ptr<BlockChunk> b) {
std::shared_ptr<BlockChunk> src;
std::shared_ptr<BlockChunk> res;
if (a->generated) {
res = a;
src = b;
}
else {
res = b;
src = a;
}
for (unsigned int i = 0; i < 4096; i++) {
if (src->getBlock(i) != DefinitionAtlas::INVALID) res->setBlock(i, src->getBlock(i));
}
res->generated = src->generated || res->generated;
assert(res != nullptr);
return res;
}

View File

@ -23,22 +23,26 @@ public:
MapGen(unsigned int seed, DefinitionAtlas& atlas, BiomeAtlas& biome);
chunk_partials_map generateMapBlock(glm::ivec3 mbPos);
// Combine two chunk partials, or a chunk and a chunk partial.
// If both are partials `b` takes preference, if one is a fully generated chunk the partial takes preference.
static std::shared_ptr<BlockChunk> combinePartials(std::shared_ptr<BlockChunk> a, std::shared_ptr<BlockChunk> b);
private:
// Generate a chunk at `worldPos`, and place it & any partials in `chunks`.
// Generate a chunk at `worldPos`, and place it and any partials in `chunks`.
void generateChunk(chunk_partials_map& chunks, glm::ivec3 worldPos);
// Build the density map for a job.
void buildDensityMap(MapGenJob* job, glm::ivec3 worldPos);
// Build the elevation map for a chunk, which uses the `chunks` partials array for efficiency.
// Build the elevation map for a chunk, which uses the `chunks` array for efficiency.
void buildElevationMap(chunk_partials_map& chunks, chunk_partial& chunk);
// Fill a chunk with blocks and any structures that should be included, may generate partials.
// Returns chunks in the `chunk` vector.
void fillChunkBlocks(chunk_partial& chunk);
// Generate blocks and structures on a chunk, respectively. generateStructures can create partials.
void generateBlocks(chunk_partial& chunk);
void generateStructures(chunk_partials_map& chunks, chunk_partial& chunk);
// Fill a chunk with structures
void fillChunkStructures(chunk_partial& chunk);
// Place block in the `chunks` array, creates a partial if necessary.
static void setBlock(glm::ivec3 worldPos, unsigned int block, chunk_partials_map& chunks);
unsigned int seed = 0;

View File

@ -50,14 +50,17 @@ void ServerWorld::update(double delta) {
}
auto finished = genStream->update();
generatedChunks = static_cast<int>(finished.size());
generatedChunks = static_cast<int>(finished->size());
//TODO: Make this finish MapBlocks @ a time
for (const auto& chunk : finished) {
// TODO: Could be optimized if WorldGenStream passed structs for whole mapBlocks at a time, however
// that gets complicated quick when considering partials being passed back for other mapblocks,
// it might be worth the effort to reduce user list iterations, but it might not be.
for (const auto& chunk : *finished) {
dimension.setChunk(chunk);
// TODO: Make this only happen once per mapblock, not for every chunk
glm::ivec3 mapBlockPos = Space::MapBlock::world::fromChunk(chunk->pos);
if (chunk->generated) dimension.getMapBlock(mapBlockPos)->generated = true;
unsigned long long mapBlockIntegrity = dimension.getMapBlockIntegrity(mapBlockPos);
for (auto& client : clientList.clients) {
@ -115,7 +118,8 @@ void ServerWorld::changedChunks(ServerClient& client) {
for (const auto &c : generateOrder) {
glm::vec3 mapBlockPos = mapBlock + c;
if (!isInBounds(mapBlockPos, oldBounds)) {
if (dimension.getMapBlock(mapBlockPos) != nullptr) {
auto existing = dimension.getMapBlock(mapBlockPos);
if (existing != nullptr && existing->generated) {
mapBlocksExisting++;
sendMapBlock(mapBlockPos, client);
}
@ -130,7 +134,7 @@ void ServerWorld::changedChunks(ServerClient& client) {
}
bool ServerWorld::generateMapBlock(glm::ivec3 pos) {
if(!generateQueueMap.count(pos) && !dimension.getMapBlock(pos)) {
if(!generateQueueMap.count(pos) && (!dimension.getMapBlock(pos) || !dimension.getMapBlock(pos)->generated)) {
generateQueueMap.insert(pos);
generateQueueList.push_back(pos);
return true;
@ -156,7 +160,7 @@ void ServerWorld::sendMapBlock(const glm::ivec3& pos, ServerClient &peer) {
assert(mapBlock != nullptr);
for (unsigned short i = 0; i < 64; i++) {
sendChunk((*mapBlock)[i], peer);
if ((*mapBlock)[i] != nullptr) sendChunk((*mapBlock)[i], peer);
}
}
}

View File

@ -27,8 +27,8 @@ bool WorldGenStream::tryToQueue(glm::vec3 pos) {
return sizeOfQueue + 1 < TOTAL_QUEUE_SIZE;
}
std::vector<std::shared_ptr<BlockChunk>> WorldGenStream::update() {
std::vector<std::shared_ptr<BlockChunk>> finishedChunks;
std::unique_ptr<std::vector<std::shared_ptr<BlockChunk>>> WorldGenStream::update() {
auto finishedChunks = std::make_unique<std::vector<std::shared_ptr<BlockChunk>>>();
for (auto& t : threads) {
for (auto& u : t.tasks) {
@ -36,7 +36,7 @@ std::vector<std::shared_ptr<BlockChunk>> WorldGenStream::update() {
if (!u.chunks.empty()) {
for (auto chunk : u.chunks) {
finishedChunks.push_back(std::shared_ptr<BlockChunk>(chunk.second.second));
finishedChunks->push_back(std::shared_ptr<BlockChunk>(chunk.second.second));
}
u.chunks.clear();
}

View File

@ -30,7 +30,7 @@ public:
//Will return a vector of BlockChunk pointers containing finished chunks.
//Frees up the threads and starts new units.
std::vector<std::shared_ptr<BlockChunk>> update();
std::unique_ptr<std::vector<std::shared_ptr<BlockChunk>>> update();
struct Unit {
glm::ivec3 pos {};

View File

@ -187,12 +187,8 @@ void LocalDimension::attemptMeshChunk(const sptr<BlockChunk>& chunk, bool update
}
if (allExists) {
if (chunk->shouldRender()) {
pendingMesh.push_back(chunk->pos);
}
else {
removeMeshChunk(chunk->pos);
}
if (chunk->shouldHaveMesh) pendingMesh.push_back(chunk->pos);
else removeMeshChunk(chunk->pos);
chunk->dirty = false; //TODO: Make dirty work
}

View File

@ -13,6 +13,10 @@ bool ServerDimension::setBlock(glm::ivec3 pos, unsigned int block) {
}
void ServerDimension::setChunk(std::shared_ptr<BlockChunk> chunk) {
// Combine partials if there are any
std::shared_ptr<BlockChunk> existing = getChunk(chunk->pos);
if (existing != nullptr) chunk = MapGen::combinePartials(chunk, existing);
Dimension::setChunk(chunk);
glm::vec3 mb = Space::MapBlock::world::fromChunk(chunk->pos);
mapBlockIntegrity[mb] = mapBlockIntegrity[mb] + 1;

View File

@ -5,6 +5,7 @@
#pragma once
#include "Dimension.h"
#include "../def/gen/MapGen.h"
#include "../lua/api/type/ServerLuaEntity.h"
class ServerDimension : public Dimension {

View File

@ -14,66 +14,9 @@ BlockChunk::BlockChunk(const std::array<unsigned int, 4096>& blocks, const std::
BlockChunk::BlockChunk(const std::array<unsigned int, 4096>& blocks, const std::array<unsigned short, 4096>& biomes, glm::ivec3 pos) :
blocks(std::move(blocks)),
biomes(std::move(biomes)),
pos(pos) {
for (unsigned int block : this->blocks) {
if (block != DefinitionAtlas::AIR) {
empty = false;
fullBlocks++;
}
}
shouldHaveMesh = empty;
}
bool BlockChunk::shouldRender() {
return shouldHaveMesh;
}
bool BlockChunk::setBlock(const glm::ivec3& pos, unsigned int block) {
if (pos.x > 15 || pos.x < 0 || pos.y > 15 || pos.y < 0 || pos.z > 15 || pos.z < 0) return false;
unsigned int ind = Space::Block::index(pos);
if (blocks[ind] != block) {
if (block == DefinitionAtlas::AIR) {
fullBlocks--;
if (fullBlocks == 0) {
empty = true;
shouldHaveMesh = false;
}
}
else if (blocks[ind] == DefinitionAtlas::AIR) {
if (fullBlocks == 0) shouldHaveMesh = true;
empty = false;
fullBlocks++;
}
blocks[ind] = block;
return true;
}
return false;
}
unsigned int BlockChunk::getBlock(unsigned int ind) const {
if (ind >= 4096) return DefinitionAtlas::INVALID;
return blocks[ind];
}
unsigned int BlockChunk::getBlock(const glm::ivec3& pos) const {
unsigned int ind = Space::Block::index(pos);
if (ind >= 4096) return DefinitionAtlas::INVALID;
return blocks[ind];
}
unsigned short BlockChunk::getBiome(unsigned int ind) const {
if (ind >= 4096) return BiomeAtlas::INVALID;
return biomes[ind];
}
unsigned short BlockChunk::getBiome(const glm::ivec3& pos) const {
unsigned int ind = Space::Block::index(pos);
if (ind >= 4096) return BiomeAtlas::INVALID;
return biomes[ind];
pos(pos),
generated(true) {
calcNonAirBlocks();
}
Packet BlockChunk::serialize() {
@ -175,14 +118,14 @@ void BlockChunk::deserialize(Packet& packet) {
end: {}
}
void BlockChunk::mgRegenEmpty() {
fullBlocks = 0;
void BlockChunk::calcNonAirBlocks() {
nonAirBlocks = 0;
empty = true;
for (unsigned int block : this->blocks) {
if (block != DefinitionAtlas::AIR) {
empty = false;
fullBlocks++;
nonAirBlocks++;
}
}

View File

@ -4,7 +4,6 @@
#pragma once
#include <list>
#include <vector>
#include <iostream>
#include <glm/vec3.hpp>
@ -16,9 +15,6 @@
#include "../../def/DefinitionAtlas.h"
#include "../../util/net/Serializer.h"
#include "../../util/net/Deserializer.h"
#include "../../game/scene/world/graph/MeshChunk.h"
typedef unsigned int uint;
class BlockChunk {
public:
@ -26,20 +22,22 @@ public:
explicit BlockChunk(const std::array<unsigned int, 4096>& blocks, const std::array<unsigned short, 4096>& biomes);
BlockChunk(const std::array<unsigned int, 4096>& blocks, const std::array<unsigned short, 4096>& biomes, glm::ivec3 pos);
bool shouldRender();
inline bool setBlock(unsigned int ind, unsigned int blk);
inline bool setBlock(const glm::ivec3& pos, unsigned int blk);
bool setBlock(const glm::ivec3& pos, unsigned int ind);
inline unsigned int getBlock(unsigned int ind) const;
inline unsigned int getBlock(const glm::ivec3& pos) const;
unsigned int getBlock(unsigned int ind) const;
unsigned int getBlock(const glm::ivec3& pos) const;
// bool setBiome(const glm::ivec3& pos, unsigned int ind);
unsigned short getBiome(unsigned int ind) const;
unsigned short getBiome(const glm::ivec3& pos) const;
inline unsigned short getBiome(unsigned int ind) const;
inline unsigned short getBiome(const glm::ivec3& pos) const;
void setSunlight(unsigned int ind, unsigned char val);
int getSunlight(unsigned int ind);
void setBlocklight(unsigned int ind, unsigned char val);
int getBlocklight(unsigned int ind);
inline void setSunlight(unsigned int ind, unsigned char val);
inline void setBlocklight(unsigned int ind, unsigned char val);
inline int getSunlight(unsigned int ind);
inline int getBlocklight(unsigned int ind);
Packet serialize();
void deserialize(Packet& packet);
@ -47,32 +45,78 @@ public:
bool shouldHaveMesh = true;
bool dirty = true;
bool generated = false;
glm::ivec3 pos;
private:
std::array<unsigned int, 4096> blocks {};
std::array<unsigned short, 4096> biomes {};
std::array<unsigned char, 4096> lighting {};
unsigned short fullBlocks = 0;
bool empty = true;
unsigned short nonAirBlocks = 0;
//Exclusive Access for MapGen to speed up chunk creation
void mgRegenEmpty();
friend class MapGen;
void calcNonAirBlocks();
};
inline bool BlockChunk::setBlock(unsigned int ind, unsigned int blk) {
if (ind >= 4096) return false;
if (blocks[ind] != blk) {
if (blk == DefinitionAtlas::AIR) {
if ((nonAirBlocks = fmax(nonAirBlocks - 1, 0)) == 0) {
empty = true;
shouldHaveMesh = false;
}
}
else if (blocks[ind] == DefinitionAtlas::AIR) {
if (nonAirBlocks == 0) shouldHaveMesh = true;
empty = false;
nonAirBlocks++;
}
blocks[ind] = blk;
return true;
}
return false;
}
inline bool BlockChunk::setBlock(const glm::ivec3& pos, unsigned int blk) {
if (pos.x > 15 || pos.x < 0 || pos.y > 15 || pos.y < 0 || pos.z > 15 || pos.z < 0) return false;
return setBlock(Space::Block::index(pos), blk);
}
inline unsigned int BlockChunk::getBlock(unsigned int ind) const {
if (ind > 4096) return DefinitionAtlas::INVALID;
return blocks[ind];
}
inline unsigned int BlockChunk::getBlock(const glm::ivec3& pos) const {
if (pos.x > 15 || pos.x < 0 || pos.y > 15 || pos.y < 0 || pos.z > 15 || pos.z < 0) return DefinitionAtlas::INVALID;
return getBlock(Space::Block::index(pos));
}
inline unsigned short BlockChunk::getBiome(unsigned int ind) const {
if (ind >= 4096) return BiomeAtlas::INVALID;
return biomes[ind];
}
inline unsigned short BlockChunk::getBiome(const glm::ivec3& pos) const {
if (pos.x > 15 || pos.x < 0 || pos.y > 15 || pos.y < 0 || pos.z > 15 || pos.z < 0) return BiomeAtlas::INVALID;
return getBiome(Space::Block::index(pos));
}
inline void BlockChunk::setSunlight(unsigned int ind, unsigned char val) {
lighting[ind] = (lighting[ind] & 0xF) | (val << 4);
}
inline int BlockChunk::getSunlight(unsigned int ind) {
return (lighting[ind] >> 4) & 0xF;
}
inline void BlockChunk::setBlocklight(unsigned int ind, unsigned char val) {
lighting[ind] = (lighting[ind] & 0xF0) | val;
}
inline int BlockChunk::getSunlight(unsigned int ind) {
return (lighting[ind] >> 4) & 0xF;
}
inline int BlockChunk::getBlocklight(unsigned int ind) {
return (lighting[ind]) & 0xF;
}

View File

@ -16,6 +16,8 @@ public:
std::shared_ptr<BlockChunk> operator[](int index);
void set(int index, std::shared_ptr<BlockChunk> chunk);
bool generated = false;
private:
glm::ivec3 pos {};
std::array<std::shared_ptr<BlockChunk>, 64> blockChunks;