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