OpenMiner/server/source/world/TerrainGenerator.cpp

200 lines
6.1 KiB
C++

/*
* =====================================================================================
*
* Filename: TerrainGenerator.cpp
*
* Description:
*
* Created: 12/06/2018 22:47:04
*
* Author: Quentin Bazin, <quent42340@gmail.com>
*
* =====================================================================================
*/
#include <glm/gtc/noise.hpp>
#include "Config.hpp"
#include "BlockType.hpp"
#include "ServerChunk.hpp"
#include "TerrainGenerator.hpp"
#include "World.hpp"
void TerrainGenerator::generate(ServerChunk &chunk) const {
// lightTestGeneration(chunk);
// basicGeneration(chunk);
testCraftGeneration(chunk);
}
void TerrainGenerator::lightTestGeneration(ServerChunk &chunk) const {
for(u8 y = 0 ; y < CHUNK_HEIGHT ; y++) {
for(u8 z = 0 ; z < CHUNK_DEPTH ; z++) {
for(u8 x = 0 ; x < CHUNK_WIDTH ; x++) {
if (y == 4)
chunk.setBlockRaw(x, y, z, BlockType::Stone);
else if (y == 10 && (z > 4 || chunk.z() != 0))
chunk.setBlockRaw(x, y, z, BlockType::Stone);
}
}
}
if (chunk.y() == 0) {
for(u8 z = 0 ; z < CHUNK_DEPTH ; z++) {
for(u8 x = 0 ; x < CHUNK_WIDTH ; x++) {
chunk.lightmap().addSunlight(x, 31, z, 15);
}
}
}
// Chunk *topChunk = chunk.getSurroundingChunk(Chunk::Top);
// if (topChunk) {
// for(u8 z = 0 ; z < CHUNK_DEPTH ; z++) {
// for(u8 x = 0 ; x < CHUNK_WIDTH ; x++) {
// int sunlightLevel = topChunk->lightmap().getSunlight(x, 0, z);
// if (sunlightLevel) {
// chunk.lightmap().addSunlight(x, CHUNK_HEIGHT - 1, z, sunlightLevel);
// }
// }
// }
// }
}
void TerrainGenerator::basicGeneration(ServerChunk &chunk) const {
for(u8 z = 0 ; z < CHUNK_DEPTH ; z++) {
for(u8 x = 0 ; x < CHUNK_WIDTH ; x++) {
float n = noise2d((x + chunk.x() * CHUNK_WIDTH) / 256.0, (z + chunk.z() * CHUNK_DEPTH) / 256.0, 5, 0.5) * 4;
float h = 10 + n * 2;
for(u8 y = 0 ; y < CHUNK_HEIGHT ; y++) {
if(y + chunk.y() * CHUNK_HEIGHT < h) {
chunk.setBlockRaw(x, y, z, 1);
}
else if (y == CHUNK_HEIGHT - 1) {
chunk.lightmap().addSunlight(x, y, z, 15);
}
}
}
}
}
void TerrainGenerator::testCraftGeneration(ServerChunk &chunk) const {
srand(1337);
Chunk *topChunk = chunk.getSurroundingChunk(Chunk::Top);
for(int z = 0 ; z < CHUNK_DEPTH ; z++) {
for(int x = 0 ; x < CHUNK_WIDTH ; x++) {
// Land height
float n = noise2d((x + chunk.x() * CHUNK_WIDTH) / 256.0, (z + chunk.z() * CHUNK_DEPTH) / 256.0, 4, 0.5) * 4;
float h = 10 + n * 2;
// Land blocks
for(int y = 0 ; y < CHUNK_HEIGHT ; y++) {
// Are we above "ground" level?
if(y + chunk.y() * CHUNK_HEIGHT >= h) {
// If we are not yet up to sea level, fill with water blocks
if(y + chunk.y() * CHUNK_HEIGHT < SEALEVEL) {
chunk.setBlockRaw(x, y, z, BlockType::Water);
}
// Otherwise we are in the air, so try to make a tree
else if(chunk.getBlock(x, y - 1, z) == BlockType::Grass && (rand() % 256) == 0 && n < 4) {
// Trunk
h = (rand() & 0x3) + 3;
for(int i = 0 ; i < h ; i++) {
chunk.setBlockRaw(x, y + i, z, BlockType::Wood);
}
// Leaves
for(int ix = -3 ; ix <= 3 ; ix++) {
for(int iy = -3 ; iy <= 3 ; iy++) {
for(int iz = -3 ; iz <= 3 ; iz++) {
if(ix * ix + iy * iy + iz * iz < 8 + (rand() & 1) && !chunk.getBlock(x + ix, y + h + iy, z + iz)) {
chunk.setBlockRaw(x + ix, y + h + iy, z + iz, BlockType::Leaves);
}
}
}
}
}
// Or a flower
else if(chunk.getBlock(x, y - 1, z) == BlockType::Grass && (rand() & 0xff) == 0) {
chunk.setBlockRaw(x, y, z, BlockType::Flower);
}
// If we are on the top block of the chunk, add sunlight
else if (y == CHUNK_HEIGHT - 1) {
chunk.lightmap().addSunlight(x, y, z, 15);
}
}
else {
// Random value used to determine land type
float r = noise3d_abs((x + chunk.x() * CHUNK_WIDTH) / 256.0, (y + chunk.y() * CHUNK_HEIGHT) / 256.0 + 16, (z + chunk.z() * CHUNK_DEPTH) / 256.0, 5, 0.5) * 4;
// Sand layer
if(n * 4 + r * 5 < 4) {
chunk.setBlockRaw(x, y, z, BlockType::Sand);
}
// Dirt layer, but use grass blocks for the top
else if(n + r * 5 < 6 * 8 && n * 10 + r * 5 > 30) {
chunk.setBlockRaw(x, y, z, (h < SEALEVEL - 5 || y + chunk.y() * CHUNK_HEIGHT < h - 1) ? BlockType::Dirt : BlockType::Grass);
}
else {
chunk.setBlockRaw(x, y, z, BlockType::Stone);
}
// Caves
float n2 = noise2d((x + chunk.x() * CHUNK_WIDTH) / 256.0, (z + chunk.z() * CHUNK_DEPTH) / 256.0, 8, 0.3) * 4;
float r2 = noise3d_abs((x + chunk.x() * CHUNK_WIDTH) / 512.0f, (y + chunk.y() * CHUNK_HEIGHT) / 512.0f, (z + chunk.z() * CHUNK_DEPTH) / 512.0f, 4, 0.1);
float r3 = noise3d_abs((x + chunk.x() * CHUNK_WIDTH) / 512.0f, (y + chunk.y() * CHUNK_HEIGHT) / 128.0f, (z + chunk.z() * CHUNK_DEPTH) / 512.0f, 4, 1);
float r4 = n2 * 5 + r2 * r3 * 20;
if (r4 > 6 && r4 < 8) {
chunk.setBlockRaw(x, y - 1, z, 0);
chunk.setBlockRaw(x, y, z, 0);
chunk.setBlockRaw(x, y + 1, z, 0);
}
else if (r < 0.3) {
chunk.setBlockRaw(x, y, z, BlockType::CoalOre);
}
else if (r > 0.3 && r < 0.5) {
chunk.setBlockRaw(x, y, z, BlockType::IronOre);
}
else if (r4 > 8.2 && r4 < 10) {
chunk.setBlockRaw(x, y, z, BlockType::Stone);
}
}
if (topChunk) {
int sunlightLevel = topChunk->lightmap().getSunlight(x, 0, z);
if (sunlightLevel) {
chunk.lightmap().addSunlight(x, CHUNK_HEIGHT - 1, z, sunlightLevel);
}
}
}
}
}
}
float TerrainGenerator::noise2d(float x, float y, int octaves, float persistence) {
float sum = 0;
float strength = 1.0;
float scale = 1.0;
for(int i = 0 ; i < octaves ; i++) {
sum += strength * glm::simplex(glm::vec2(x, y) * scale);
scale *= 2.0;
strength *= persistence;
}
return sum;
}
float TerrainGenerator::noise3d_abs(float x, float y, float z, int octaves, float persistence) {
float sum = 0;
float strength = 1.0;
float scale = 1.0;
for(int i = 0 ; i < octaves ; i++) {
sum += strength * fabs(glm::simplex(glm::vec3(x, y, z) * scale));
scale *= 2.0;
strength *= persistence;
}
return sum;
}