1 /** 2 Copyright: Copyright (c) 2013-2018 Andrey Penechko. 3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 4 Authors: Andrey Penechko. 5 */ 6 module voxelman.world.gen.generators; 7 8 import voxelman.math : ivec3, svec2, svec3, SimplexNoise; 9 import voxelman.container.cache; 10 11 import voxelman.core.config; 12 import voxelman.world.storage.coordinates; 13 import voxelman.world.gen.utils; 14 import voxelman.world.gen.generator; 15 16 17 final class Generator2d3d : IGenerator 18 { 19 static string name = "voxelman.Generator2d3d"; 20 override ChunkGeneratorResult generateChunk( 21 svec3 cwp, 22 ref BlockId[CHUNK_SIZE_CUBE] blocks) const 23 { 24 svec2 cachePos = cwp.xz; 25 ivec3 chunkOffset = ivec3(cwp) * CHUNK_SIZE; 26 27 HeightmapChunkData* heightMap; 28 if (auto val = heightmapCache.get(cachePos)) 29 { 30 atomicOp!"+="(cache_hits, 1); 31 heightMap = val; 32 } 33 else 34 { 35 atomicOp!"+="(cache_misses, 1); 36 heightMap = heightmapCache.put(cachePos); 37 heightMap.generate(chunkOffset); 38 } 39 40 if (chunkOffset.y > heightMap.maxHeight && 41 chunkOffset.y > 0) 42 { 43 return ChunkGeneratorResult(true, AIR); 44 } 45 else 46 { 47 foreach(i; 0..CHUNK_SIZE_CUBE) 48 { 49 ivec3 blockWorldPos; 50 blockWorldPos.x = i & CHUNK_SIZE_BITS; 51 blockWorldPos.y = (i / CHUNK_SIZE_SQR) & CHUNK_SIZE_BITS; 52 blockWorldPos.z = (i / CHUNK_SIZE) & CHUNK_SIZE_BITS; 53 int height = heightMap.heightMap[blockWorldPos.z * CHUNK_SIZE + blockWorldPos.x]; 54 blockWorldPos += chunkOffset; 55 blocks[i] = generateBlock(blockWorldPos, height); 56 } 57 return ChunkGeneratorResult(false); 58 } 59 } 60 61 static BlockId generateBlock(ivec3 blockWorldPos, int height) pure 62 { 63 enum NOISE_SCALE_3D = 42; 64 enum NOISE_TRESHOLD_3D = -0.6; 65 if (blockWorldPos.y > height) { 66 if (blockWorldPos.y > 0) 67 return AIR; 68 else 69 return WATER; 70 } 71 72 double noise3d = SimplexNoise.noise(cast(double)(blockWorldPos.x)/NOISE_SCALE_3D, 73 cast(double)(blockWorldPos.y)/NOISE_SCALE_3D, cast(double)(blockWorldPos.z)/NOISE_SCALE_3D); 74 if (noise3d < NOISE_TRESHOLD_3D) return AIR; 75 76 if (height + 5 < 0) 77 { 78 if (height - blockWorldPos.y < 10) return SAND; 79 else return STONE; 80 } 81 else 82 { 83 if (blockWorldPos.y == height) return GRASS; 84 else if (blockWorldPos.y > height - 10) return DIRT; 85 else return STONE; 86 } 87 } 88 89 static Cache!(svec2, HeightmapChunkData, 16) heightmapCache; 90 } 91 92 shared size_t cache_hits; 93 shared size_t cache_misses; 94 import core.atomic; 95 96 final class Generator2d : IGenerator 97 { 98 static string name = "voxelman.Generator2d"; 99 override ChunkGeneratorResult generateChunk( 100 svec3 cwp, 101 ref BlockId[CHUNK_SIZE_CUBE] blocks) const 102 { 103 svec2 cachePos = cwp.xz; 104 ivec3 chunkOffset = ivec3(cwp) * CHUNK_SIZE; 105 106 HeightmapChunkData* heightMap; 107 if (auto val = heightmapCache.get(cachePos)) 108 { 109 atomicOp!"+="(cache_hits, 1); 110 heightMap = val; 111 } 112 else 113 { 114 atomicOp!"+="(cache_misses, 1); 115 heightMap = heightmapCache.put(cachePos); 116 heightMap.generate(chunkOffset); 117 } 118 119 if (chunkOffset.y > heightMap.maxHeight && 120 chunkOffset.y > 0) 121 { 122 return ChunkGeneratorResult(true, AIR); 123 } 124 else 125 { 126 foreach(i; 0..CHUNK_SIZE_CUBE) 127 { 128 int bx = i & CHUNK_SIZE_BITS; 129 int by = (i / CHUNK_SIZE_SQR) & CHUNK_SIZE_BITS; 130 int bz = (i / CHUNK_SIZE) & CHUNK_SIZE_BITS; 131 int blockY = chunkOffset.y + by; 132 int height = heightMap.heightMap[bz * CHUNK_SIZE + bx]; 133 blocks[i] = generateBlock(blockY, height); 134 } 135 return ChunkGeneratorResult(false); 136 } 137 } 138 139 static BlockId generateBlock(int blockY, int height) pure 140 { 141 if (blockY > height) { 142 if (blockY > 0) 143 return AIR; 144 else 145 return WATER; 146 } 147 148 if (height - 5 < 0) 149 { 150 if (height - blockY < 10) return SAND; 151 else return STONE; 152 } 153 else 154 { 155 if (blockY == height) return GRASS; 156 else if (blockY > height - 10) return DIRT; 157 else return STONE; 158 } 159 } 160 161 static Cache!(svec2, HeightmapChunkData, 16) heightmapCache; 162 } 163 164 final class GeneratorFlat : IGenerator 165 { 166 static string name = "voxelman.GeneratorFlat"; 167 override ChunkGeneratorResult generateChunk( 168 svec3 chunkOffset, 169 ref BlockId[CHUNK_SIZE_CUBE] blocks) const 170 { 171 if (chunkOffset.y >= 0) 172 return ChunkGeneratorResult(true, AIR); 173 else 174 return ChunkGeneratorResult(true, STONE); 175 } 176 }