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 }