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 }