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.core.chunkgen; 7 8 import std.experimental.logger; 9 import std.concurrency : Tid, send, receive; 10 import std.variant : Variant; 11 import core.atomic : atomicLoad; 12 import std.conv : to; 13 import core.exception : Throwable; 14 15 import dlib.math.vector : ivec3; 16 17 import anchovy.simplex; 18 import voxelman.core.block; 19 import voxelman.core.config; 20 import voxelman.storage.chunk; 21 import voxelman.storage.chunkprovider; 22 import voxelman.storage.coordinates; 23 24 25 alias Generator = Generator2d; 26 //alias Generator = Generator2d3d; 27 //alias Generator = TestGeneratorSmallCubes; 28 //alias Generator = TestGeneratorSmallCubes2; 29 //alias Generator = TestGeneratorSmallCubes3; 30 31 struct ChunkGenResult 32 { 33 BlockData blockData; 34 ChunkWorldPos position; 35 TimestampType timestamp; 36 } 37 38 void chunkGenWorkerThread(Tid mainTid) 39 { 40 try 41 { 42 shared(bool)* isRunning; 43 bool isRunningLocal = true; 44 receive( (shared(bool)* _isRunning){isRunning = _isRunning;} ); 45 46 while (atomicLoad(*isRunning) && isRunningLocal) 47 { 48 receive( 49 (immutable(LoadSnapshotMessage)* message){ 50 chunkGenWorker(cast(LoadSnapshotMessage*)message, mainTid); 51 }, 52 (Variant v){isRunningLocal = false;} 53 ); 54 } 55 } 56 catch(Throwable t) 57 { 58 error(t.to!string, " from gen worker"); 59 throw t; 60 } 61 } 62 63 // Gen single chunk 64 void chunkGenWorker(LoadSnapshotMessage* message, Tid mainThread) 65 { 66 ChunkWorldPos cwp = message.cwp; 67 int wx = cwp.x, wy = cwp.y, wz = cwp.z; 68 69 BlockData bd; 70 bd.blocks = message.blockBuffer; 71 bd.convertToArray(); 72 bd.uniform = false; 73 bool uniform = true; 74 75 Generator generator = Generator(cwp.vector * CHUNK_SIZE); 76 generator.genPerChunkData(); 77 78 bd.blocks[0] = generator.generateBlock(0, 0, 0); 79 BlockType type = bd.blocks[0]; 80 81 int bx, by, bz; 82 foreach(i; 1..CHUNK_SIZE_CUBE) 83 { 84 bx = i & CHUNK_SIZE_BITS; 85 by = (i / CHUNK_SIZE_SQR) & CHUNK_SIZE_BITS; 86 bz = (i / CHUNK_SIZE) & CHUNK_SIZE_BITS; 87 88 // Actual block gen 89 bd.blocks[i] = generator.generateBlock(bx, by, bz); 90 91 if(uniform && bd.blocks[i] != type) 92 { 93 uniform = false; 94 } 95 } 96 97 bd.uniform = uniform; 98 if(uniform) { 99 bd.uniformType = type; 100 } 101 102 auto res = new SnapshotLoadedMessage(message.cwp, BlockDataSnapshot(bd)); 103 mainThread.send(cast(immutable(SnapshotLoadedMessage)*)res); 104 } 105 106 struct Generator2d3d 107 { 108 ivec3 chunkOffset; 109 110 private int[CHUNK_SIZE_SQR] heightMap = void; 111 112 void genPerChunkData() 113 { 114 genPerChunkData2d(heightMap[], chunkOffset); 115 } 116 117 BlockType generateBlock(int x, int y, int z) 118 { 119 int height = heightMap[z * CHUNK_SIZE + x]; 120 int blockY = chunkOffset.y + y; 121 if (blockY > height) return 1; 122 123 float noise = Simplex.noise(cast(float)(chunkOffset.x+x)/42, 124 cast(float)(chunkOffset.y+y)/42, cast(float)(chunkOffset.z+z)/42); 125 if (noise < -0.1) return 1; 126 127 if (blockY == height) return 2; 128 else if (blockY > height - 10) return 3; 129 else return 4; 130 } 131 } 132 133 struct Generator2d 134 { 135 ivec3 chunkOffset; 136 137 private int[CHUNK_SIZE_SQR] heightMap = void; 138 139 void genPerChunkData() 140 { 141 genPerChunkData2d(heightMap[], chunkOffset); 142 } 143 144 BlockType generateBlock(int x, int y, int z) 145 { 146 int height = heightMap[z * CHUNK_SIZE + x]; 147 int blockY = chunkOffset.y + y; 148 if (blockY > height) return 1; 149 if (blockY == height) return 2; 150 else if (blockY > height - 10) return 3; 151 else return 4; 152 } 153 } 154 155 struct TestGeneratorSmallCubes 156 { 157 ivec3 chunkOffset; 158 void genPerChunkData(){} 159 160 BlockType generateBlock(int x, int y, int z) 161 { 162 if (x % 2 == 0 && y % 2 == 0 && z % 2 == 0) return 2; 163 else return 1; 164 } 165 } 166 167 struct TestGeneratorSmallCubes2 168 { 169 ivec3 chunkOffset; 170 void genPerChunkData(){} 171 172 BlockType generateBlock(int x, int y, int z) 173 { 174 if (x % 4 == 0 && y % 4 == 0 && z % 4 == 0) return 2; 175 else return 1; 176 } 177 } 178 179 struct TestGeneratorSmallCubes3 180 { 181 enum cubesSizes = 4; 182 enum cubeOffsets = 16; 183 ivec3 chunkOffset; 184 void genPerChunkData(){} 185 186 BlockType generateBlock(int x, int y, int z) 187 { 188 if (x % cubeOffsets < cubesSizes && 189 y % cubeOffsets < cubesSizes && 190 z % cubeOffsets < cubesSizes) return 2; 191 else return 1; 192 } 193 } 194 195 float noise2d(int x, int z) 196 { 197 enum NUM_OCTAVES = 6; 198 enum DIVIDER = 50; // bigger - smoother 199 enum HEIGHT_MODIFIER = 4; // bigger - higher 200 201 float noise = 0.0; 202 foreach(i; 1..NUM_OCTAVES+1) 203 { 204 // [-1; 1] 205 noise += Simplex.noise(cast(float)x/(DIVIDER*i), cast(float)z/(DIVIDER*i))*i*HEIGHT_MODIFIER; 206 } 207 208 return noise; 209 } 210 211 void genPerChunkData2d(int[] heightMap, ivec3 chunkOffset) 212 { 213 foreach(i, ref elem; heightMap) 214 { 215 int cx = i & CHUNK_SIZE_BITS; 216 int cz = (i / CHUNK_SIZE) & CHUNK_SIZE_BITS; 217 elem = cast(int)noise2d(chunkOffset.x + cx, chunkOffset.z + cz); 218 } 219 }