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 }