Tweaked the MapGen class to begin adding half-generated chunks.WIP Trees

* Added turbulence to biome noises, and made their sampling 2D.
* Added bees and ravens to test animation library.
* Added TNT to test large scale world destruction (some work needs to be done).
master
Nicole Collings 2020-01-30 20:54:18 -08:00
parent 8065520c71
commit dbcfe5c822
32 changed files with 263 additions and 108 deletions

View File

@ -5,79 +5,90 @@
//
#include "MapGen.h"
#include "../../util/Timer.h"
#include "NoiseSample.h"
MapGen::MapGen(unsigned int seed, DefinitionAtlas& atlas, BiomeAtlas& biomes) :
seed(seed),
atlas(atlas),
biomes(biomes) {
MapGen::MapGen(unsigned int seed, DefinitionAtlas& defs, BiomeAtlas& biomes) :
seed(seed),
defs(defs),
biomes(biomes) {
temperatureBase.SetSeed(seed);
temperatureBase.SetFrequency(0.02);
temperatureBase.SetOctaveCount(4);
// temperatureTurbulance.Set
temperature.SetSourceModule(0, temperatureBase);
temperatureTurbulence.SetSourceModule(0, temperatureBase);
temperatureTurbulence.SetRoughness(4);
temperatureTurbulence.SetFrequency(0.2);
temperature.SetSourceModule(0, temperatureTurbulence);
temperature.SetScale(0.35);
temperature.SetBias(0.25);
humidityBase.SetSeed(seed + 5);
humidityBase.SetFrequency(0.02);
humidityBase.SetOctaveCount(4);
humidity.SetSourceModule(0, humidityBase);
humidityTurbulence.SetSourceModule(0, humidityBase);
humidityTurbulence.SetRoughness(4);
humidityTurbulence.SetFrequency(0.2);
humidity.SetSourceModule(0, humidityTurbulence);
humidity.SetScale(0.5);
humidity.SetBias(0.5);
roughnessBase.SetSeed(seed + 10);
roughnessBase.SetFrequency(0.02);
roughnessBase.SetOctaveCount(4);
roughness.SetSourceModule(0, roughnessBase);
roughnessTurbulence.SetSourceModule(0, roughnessBase);
roughnessTurbulence.SetRoughness(4);
roughnessTurbulence.SetFrequency(0.2);
roughness.SetSourceModule(0, roughnessTurbulence);
roughness.SetScale(0.5);
roughness.SetBias(0.5);
treeMap.SetFrequency(1.25);
treeMap.SetOctaveCount(4);
treeAbs.SetSourceModule(0, treeMap);
}
std::vector<BlockChunk*> MapGen::generateMapBlock(glm::vec3 mbPos) {
std::array<std::pair<MapGenJob*, BlockChunk*>, 64> chunks{};
MapGen::chunk_partials_map MapGen::generateMapBlock(glm::ivec3 mbPos) {
chunk_partials_map chunks {};
// Go top down
for (short i = 3; i >= 0; i--) {
for (short j = 0; j < 4; j++) {
for (short k = 0; k < 4; k++) {
glm::vec3 pos{ j, i, k };
generateChunk(chunks, pos, pos + mbPos * 4.f);
glm::ivec3 pos = glm::ivec3(j, i, k) + (mbPos * 4);
generateChunk(chunks, pos);
}
}
}
std::vector<BlockChunk*> returns{};
returns.reserve(64);
for (auto& pair : chunks) {
returns.push_back(pair.second);
for (auto& chunk : chunks) {
// Delete MapGenJobs
delete chunk.second.first;
chunk.second.first = nullptr;
}
return returns;
return chunks;
}
void MapGen::generateChunk(std::array<std::pair<MapGenJob*, BlockChunk*>, 64>& chunks, glm::vec3 localPos, glm::vec3 worldPos) {
unsigned short index = localPos.x + 4 * (localPos.y + 4 * localPos.z);
auto& chunk = chunks[index];
chunk.first = new MapGenJob();
chunk.second = new BlockChunk();
void MapGen::generateChunk(chunk_partials_map& chunks, glm::ivec3 worldPos) {
if (chunks.count(worldPos) == 0) chunks.insert(std::pair<glm::ivec3, chunk_partial>{worldPos, {new MapGenJob(), new BlockChunk()}});
auto& chunk = chunks.at(worldPos);
chunk.second->pos = worldPos;
buildDensityMap(chunk.first, worldPos);
buildElevationMap(chunks, chunk, localPos, worldPos);
buildElevationMap(chunks, chunk);
populateChunk(chunk, worldPos);
fillChunkBlocks(chunk);
fillChunkStructures(chunk);
}
void MapGen::buildDensityMap(MapGenJob* job, const glm::vec3& worldPos) {
job->temperature = NoiseSample(temperature, worldPos, {4, 4}, false);
job->humidity = NoiseSample(humidity, worldPos, {4, 4}, false);
job->roughness = NoiseSample(roughness, worldPos, {4, 4}, false);
void MapGen::buildDensityMap(MapGenJob* job, glm::ivec3 worldPos) {
job->temperature = NoiseSample(temperature, {worldPos.x, 0, worldPos.z}, {4, 1}, true);
job->humidity = NoiseSample(humidity, {worldPos.x, 0, worldPos.z}, {4, 1}, true);
job->roughness = NoiseSample(roughness, {worldPos.x, 0, worldPos.z}, {4, 1}, true);
// TODO: This is... a travesty. Please stop doing this weird jank insertion into a
// noisesample and create a proper constructor or *something*... could probs use a module owo
auto biome = biomes.getBiomeAt(job->temperature.get({}), job->humidity.get({}), job->roughness.get({}));
auto terrain = NoiseSample({4, 4});
float offsetH = 16.f / 4.f;
@ -86,7 +97,6 @@ void MapGen::buildDensityMap(MapGenJob* job, const glm::vec3& worldPos) {
for (int i = 0; i <= 4; i++) {
for (int j = 0; j <= 4; j++) {
for (int k = 0; k <= 4; k++) {
//TODO: Find out why this is being stoopd
glm::vec3 localPos = {(offsetH * i) / 1.01f, (offsetV * j) / 1.01f, (offsetH * k) / 1.01f};
glm::vec3 pos = {(worldPos.x * 16 + offsetH * i) / 16.f, (worldPos.y * 16 + offsetV * j) / 16.f, (worldPos.z * 16 + offsetH * k) / 16.f};
auto& biome = biomes.getBiomeAt(job->temperature.get(localPos), job->humidity.get(localPos), job->roughness.get(localPos));
@ -103,10 +113,10 @@ void MapGen::buildDensityMap(MapGenJob* job, const glm::vec3& worldPos) {
}
}
void MapGen::buildElevationMap(std::array<std::pair<MapGenJob*, BlockChunk*>, 64>& chunks,
std::pair<MapGenJob*, BlockChunk*>& chunk, const glm::vec3& localPos, const glm::vec3& worldPos) {
MapGenJob* upperChunk = nullptr;
void MapGen::buildElevationMap(chunk_partials_map& chunks, chunk_partial& chunk) {
glm::ivec3 worldPos = chunk.second->pos;
MapGenJob* upperJob = nullptr;
bool createdUpperJob = false;
for (int i = 0; i < 256; i++) {
const int x = i % 16;
@ -114,20 +124,21 @@ void MapGen::buildElevationMap(std::array<std::pair<MapGenJob*, BlockChunk*>, 64
short depth = 16;
if (chunk.first->density[Space::Block::index({ x, 15, z })] > 0) {
if (localPos.y < 3) {
unsigned short index = localPos.x + 4 * (localPos.y + 1 + 4 * localPos.z);
upperChunk = chunks[index].first;
}
if (upperChunk == nullptr) {
upperChunk = new MapGenJob();
buildDensityMap(upperChunk, worldPos + glm::vec3{ 0, 1, 0 });
}
if (chunk.first->density[Space::Block::index({x, 15, z})] > 0) {
if (!upperJob) {
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;
}
}
for (int j = 0; j < 16; j++) {
int ind = Space::Block::index({ x, j, z });
if (upperChunk->density[ind] <= 0) {
if (upperJob->density[Space::Block::index({ x, j, z })] <= 0) {
depth = j;
break;
}
@ -137,23 +148,18 @@ void MapGen::buildElevationMap(std::array<std::pair<MapGenJob*, BlockChunk*>, 64
for (int y = 15; y >= 0; y--) {
int ind = Space::Block::index({ x, y, z });
if (chunk.first->density[ind] > 0) {
depth = std::min(depth + 1, 16);
}
else depth = 0;
depth = (chunk.first->density[ind] > 0 ? std::min(depth + 1, 16) : 0);
chunk.first->depth[ind] = depth + (chunk.first->density[ind] - static_cast<int>(chunk.first->density[ind]));
}
}
if (localPos.y >= 3) delete upperChunk;
if (createdUpperJob) delete upperJob;
}
void MapGen::populateChunk(std::pair<MapGenJob*, BlockChunk*>& chunk, const glm::vec3& worldPos) {
void MapGen::fillChunkBlocks(chunk_partial& chunk) {
glm::ivec3 lp;
for (int m = 0; m < 4096; m++) {
for (unsigned short m = 0; m < 4096; m++) {
Vec::indAssignVec(m, lp);
auto biome = biomes.getBiomeAt(chunk.first->temperature.get(lp), chunk.first->humidity.get(lp), chunk.first->roughness.get(lp));
@ -161,12 +167,44 @@ void MapGen::populateChunk(std::pair<MapGenJob*, BlockChunk*>& chunk, const glm:
int d = std::floor(chunk.first->depth[m]);
chunk.second->blocks[m] =
d <= 1 ? DefinitionAtlas::AIR
chunk.second->blocks[m]
= d <= 1 ? DefinitionAtlas::AIR
: d <= 2 ? biome.topBlock
: d <= 4 ? biome.soilBlock
: biome.rockBlock;
}
chunk.second->mgRegenEmpty();
}
}
void MapGen::fillChunkStructures(chunk_partial& chunk) {
unsigned int cWood = defs.blockFromStr("zeus:default:wood").index;
unsigned int cLeaves = defs.blockFromStr("zeus:default:leaves").index;
glm::ivec3 wp = chunk.second->pos;
glm::ivec3 lp;
for (unsigned short m = 0; m < 4096; m++) {
Vec::indAssignVec(m, lp);
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) {
glm::ivec3 rp {};
for (unsigned int i = 0; i < 5; i++) {
rp.x = lp.x - 2 + i;
for (unsigned int j = 0; j < 5; j++) {
rp.z = lp.z - 2 + j;
for (unsigned int k = 0; k < 2; k++) {
rp.y = lp.y + 3 + k;
chunk.second->setBlock(rp, cLeaves);
}
}
}
for (unsigned int i = 0; i < 5; i++) {
chunk.second->setBlock(lp, cWood);
if (++lp.y > 15) break;
}
}
}
}

View File

@ -18,39 +18,45 @@ using namespace noise;
class MapGen {
public:
typedef std::pair<MapGenJob*, BlockChunk*> chunk_partial;
typedef std::unordered_map<glm::ivec3, chunk_partial, Vec::ivec3> chunk_partials_map;
MapGen(unsigned int seed, DefinitionAtlas& atlas, BiomeAtlas& biome);
std::vector<BlockChunk*> generateMapBlock(glm::vec3 mbPos);
chunk_partials_map generateMapBlock(glm::ivec3 mbPos);
private:
void generateChunk(std::array<std::pair<MapGenJob*, BlockChunk*>, 64>& chunks, glm::vec3 localPos, glm::vec3 worldPos);
// Generate a chunk at `worldPos`, and place it & any partials in `chunks`.
void generateChunk(chunk_partials_map& chunks, glm::ivec3 worldPos);
void buildDensityMap(MapGenJob* job, const glm::vec3& worldPos);
void buildElevationMap(std::array<std::pair<MapGenJob*, BlockChunk*>, 64>& chunks,
std::pair<MapGenJob*, BlockChunk*>& chunk, const glm::vec3& localPos, const glm::vec3& worldPos);
// Build the density map for a job.
void buildDensityMap(MapGenJob* job, glm::ivec3 worldPos);
void populateChunk(std::pair<MapGenJob*, BlockChunk*>& chunk, const glm::vec3& worldPos);
// Build the elevation map for a chunk, which uses the `chunks` partials 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);
// Fill a chunk with structures
void fillChunkStructures(chunk_partial& chunk);
unsigned int seed = 0;
DefinitionAtlas& atlas;
DefinitionAtlas& defs;
BiomeAtlas& biomes;
module::Perlin temperatureBase;
// module::Turbulence temperatureTurbulence;
module::Turbulence temperatureTurbulence;
module::ScaleBias temperature;
module::Perlin humidityBase;
// module::Turbulence humidityTurbulence;
module::Turbulence humidityTurbulence;
module::ScaleBias humidity;
module::Perlin roughnessBase;
// module::Turbulence roughnessTurbulence;
module::Turbulence roughnessTurbulence;
module::ScaleBias roughness;
// module::Perlin worldElevationBase;
// module::ScaleBias worldElevationScaled;
//
// module::Perlin worldFeatureBase;
// module::ScaleBias worldFeatureScaled;
//
// module::Add worldSmoothElevation;
module::Perlin treeMap;
module::Abs treeAbs;
};

View File

@ -19,8 +19,6 @@ public:
void set(glm::ivec3 pos, float value);
float get(const glm::ivec3& pos);
private:
void reserveSpace();

View File

@ -40,7 +40,7 @@ MeshPart::MeshPart(const std::vector<BlockModelVertex>& vertices, const std::vec
//Store the old positions in texUVs
vertex.texUVs.x = vertex.tex.x;
vertex.texUVs.y = vertex.tex.y;
//Generate solid coordinates for the atlas positions
//Generate solid coordinates for the defs positions
vertex.tex.x = uv.x + ((uv.z - uv.x) * vertex.tex.x);
vertex.tex.y = uv.y + ((uv.w - uv.y) * vertex.tex.y);
}

View File

@ -107,7 +107,7 @@ std::shared_ptr<AtlasRef> TextureAtlas::addImage(unsigned char *data, const std:
auto space = findImageSpace(tileWidth, tileHeight);
if (space.x < 0) {
std::cout << Log::err << "Failed to find space in dynamic atlas." << Log::endl;
std::cout << Log::err << "Failed to find space in dynamic defs." << Log::endl;
return nullptr;
}

View File

@ -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));
finishedChunks.push_back(std::shared_ptr<BlockChunk>(chunk.second.second));
}
u.chunks.clear();
}
@ -70,15 +70,12 @@ void WorldGenStream::threadFunction(WorldGenStream::Thread *thread) {
bool empty = true;
for (Unit& u : thread->tasks) {
if (!u.unlocked) {
empty = false;
u.chunks = thread->gen->generateMapBlock(u.pos);
u.unlocked = true;
break;
}
}
if (empty) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}

View File

@ -33,8 +33,8 @@ public:
std::vector<std::shared_ptr<BlockChunk>> update();
struct Unit {
glm::vec3 pos {};
std::vector<BlockChunk*> chunks {};
glm::ivec3 pos {};
MapGen::chunk_partials_map chunks {};
bool unlocked = true;
};

View File

@ -178,7 +178,6 @@ bool LocalDimension::setBlock(glm::ivec3 pos, unsigned int block) {
void LocalDimension::attemptMeshChunk(const sptr<BlockChunk>& chunk, bool updateAdjacents) {
// if (!chunk->dirty) return; //TODO
auto dirs = Vec::cardinalVectors;
bool allExists = true;
for (auto dir : dirs) {
@ -187,9 +186,15 @@ void LocalDimension::attemptMeshChunk(const sptr<BlockChunk>& chunk, bool update
}
}
if (allExists && chunk->shouldRender()) {
chunk->dirty = false;
pendingMesh.push_back(chunk->pos);
if (allExists) {
if (chunk->shouldRender()) {
pendingMesh.push_back(chunk->pos);
}
else {
removeMeshChunk(chunk->pos);
}
chunk->dirty = false; //TODO: Make dirty work
}
}

View File

@ -23,32 +23,29 @@ BlockChunk::BlockChunk(const std::array<unsigned int, 4096>& blocks, const std::
}
}
renderedEmpty = empty;
shouldHaveMesh = empty;
}
bool BlockChunk::shouldRender() {
bool should = !empty || !renderedEmpty;
renderedEmpty = true;
return should;
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 (ind >= (int)pow(16, 3)) return false;
if (blocks[ind] != block) {
if (block == 0) {
this->fullBlocks--;
if (this->fullBlocks == 0) {
std::cout << "Empty" << std::endl;
this->empty = true;
this->renderedEmpty = false;
if (block == DefinitionAtlas::AIR) {
fullBlocks--;
if (fullBlocks == 0) {
empty = true;
shouldHaveMesh = false;
}
}
else if (blocks[ind] == 0) {
this->fullBlocks++;
this->empty = false;
this->renderedEmpty = true;
else if (blocks[ind] == DefinitionAtlas::AIR) {
if (fullBlocks == 0) shouldHaveMesh = true;
empty = false;
fullBlocks++;
}
blocks[ind] = block;
@ -189,5 +186,5 @@ void BlockChunk::mgRegenEmpty() {
}
}
renderedEmpty = empty;
shouldHaveMesh = empty;
}

View File

@ -44,7 +44,7 @@ public:
Packet serialize();
void deserialize(Packet& packet);
bool renderedEmpty = true;
bool shouldHaveMesh = true;
bool dirty = true;
glm::ivec3 pos;

View File

@ -0,0 +1,5 @@
{
"name": "@aurailus:tnt",
"description": "Bet you've never seen this one before.",
"version": "0.0.1"
}

View File

@ -0,0 +1,28 @@
zepha.register_block("@aurailus:tnt:tnt", {
name = "TNT",
model = "base:block",
textures = {
"@aurailus:tnt:tnt_top",
"@aurailus:tnt:tnt_bottom",
"@aurailus:tnt:tnt_side"
},
toughness = {
hand = 3,
shovel = 1,
pick = 2
},
drop = "@aurailus:tnt:tnt",
on_break = function(pos)
local amp = 20
for i = -amp, amp do
for j = -amp, amp do
for k = -amp, amp do
local offset = v{i, j, k}
if vector.distance(v{}, offset) < amp then
zepha.set_block(vector.add(pos, offset), "air")
end
end
end
end
end
})

Binary file not shown.

After

Width:  |  Height:  |  Size: 435 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 929 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 859 B

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,3 +1,5 @@
runfile(_PATH .. "dropped_item")
runfile(_PATH .. "rabbit")
runfile(_PATH .. "raven")
runfile(_PATH .. "bee")
runfile(_PATH .. "test")

View File

@ -0,0 +1,30 @@
local friendly = false
zepha.register_entity("zeus:default:bee", {
display = "model",
display_object = "zeus:default:bee",
display_texture = "zeus:default:bee",
on_create = function(self)
self.object:set_scale(1/12)
self.object.anims:define({
fly = {1, 45}
})
self.object.anims:set_anim("fly"):play()
end,
on_update = function(self, delta)
self.object:set_pos({
x = self.object.pos.x + 0.03 * math.sin(math.rad(self.object.yaw + 90)),
y = self.object.pos.y,
z = self.object.pos.z + 0.03 * math.cos(math.rad(self.object.yaw + 90))})
self.object.yaw = self.object.yaw + 2
end
})
zepha.register_keybind("zeus:default:spawn_bee", {
description = "Spawn Bee",
default = zepha.keys.b,
on_press = function() zepha.add_entity("zeus:default:bee", vector.add(zepha.player.pos, v{0, 1.7, 0})) end
})

View File

@ -0,0 +1,48 @@
local friendly = false
zepha.register_entity("zeus:default:raven", {
display = "model",
display_object = "zeus:default:bird",
display_texture = "zeus:default:raven",
on_create = function(self)
self.object:set_scale(1/16)
self.object.anims:define({
fly = {1, 100},
preen = {101, 200},
hover = {202, 260}
})
self.object.anims:set_anim("hover"):play()
end,
on_update = function(self, delta)
local dist = vector.distance(zepha.player.pos, self.object.pos)
if dist < 5 and not self.targeting then
self.targeting = true
self.object.anims:set_anim("fly"):play()
elseif dist > 6 and self.targeting then
self.targeting = false
self.object.anims:set_anim("hover"):play()
end
if self.targeting then
self.object.pos = v {
self.object.pos.x + 0.08 * math.sin(math.rad(self.object.yaw)),
self.object.pos.y,
self.object.pos.z + 0.08 * math.cos(math.rad(self.object.yaw))
}
self.object.yaw = math.deg(math.atan2(zepha.player.pos.x - self.object.pos.x, zepha.player.pos.z - self.object.pos.z)) + 180
else
self.object.yaw = math.deg(math.atan2(zepha.player.pos.x - self.object.pos.x, zepha.player.pos.z - self.object.pos.z))
end
end
})
zepha.register_keybind("zeus:default:spawn_raven", {
description = "Spawn Raven",
default = zepha.keys.z,
on_press = function() zepha.add_entity("zeus:default:raven", vector.add(zepha.player.pos, v{0, 1.7, 0})) end
})

View File

@ -5,6 +5,7 @@ runfile(_PATH .. "entity/_index")
runfile(_PATH .. "biomes/_index")
local blockTypes = {
"@aurailus:tnt:tnt",
"zeus:default:grass_slab",
"zeus:kinetic:axle_0",
"zeus:default:stone",

Binary file not shown.

After

Width:  |  Height:  |  Size: 856 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB