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.block.blocklayermetadata;
7 
8 import voxelman.log;
9 import voxelman.geometry;
10 import voxelman.core.config;
11 import voxelman.world.block;
12 import voxelman.world.storage;
13 
14 // Chunk metadata
15 // 00 1 22_22_22_22_22_22
16 // 00 - 2 bits representing chunk's minimal solidity
17 // 1 - 1 bit representing if metadata is presented
18 // 2 - 12 bits -- solidity of each side
19 
20 enum MetadataSideMask : ushort
21 {
22 	zneg = 0b_11,
23 	zpos = 0b_11_00,
24 
25 	xpos = 0b_11_00_00,
26 	xneg = 0b_11_00_00_00,
27 
28 	ypos = 0b_11_00_00_00_00,
29 	yneg = 0b_11_00_00_00_00_00,
30 }
31 
32 // Works only with uncompressed data.
33 ushort calcChunkFullMetadata(Layer)(const ref Layer blockLayer, BlockInfoTable blockInfos)
34 {
35 	if (blockLayer.type == StorageType.uniform) {
36 		return calcChunkFullMetadata(blockLayer.getUniform!BlockId, blockInfos);
37 	} else if (blockLayer.type == StorageType.fullArray) {
38 		return calcChunkFullMetadata(blockLayer.getArray!BlockId, blockInfos);
39 	} else assert(false);
40 }
41 
42 ushort calcChunkFullMetadata(BlockId[] blocks, BlockInfoTable blockInfos)
43 {
44 	ushort sideMeta = calcChunkSideMetadata(blocks, blockInfos);
45 	ushort solidityBits = calcSolidityBits(blocks, blockInfos);
46 	return cast(ushort) (sideMeta | solidityBits<<CHUNK_SIDE_METADATA_BITS);
47 }
48 
49 ushort calcChunkFullMetadata(BlockId uniformBlock, BlockInfoTable blockInfos)
50 {
51 	ushort sideMeta = calcChunkSideMetadata(uniformBlock, blockInfos);
52 	ushort solidityBits = calcSolidityBits(uniformBlock, blockInfos);
53 	return cast(ushort) (sideMeta | solidityBits<<CHUNK_SIDE_METADATA_BITS);
54 }
55 
56 Solidity chunkSideSolidity(const ushort metadata, const CubeSide side)
57 {
58 	if (metadata & 0b1_00_00_00_00_00_00) // if metadata is presented
59 		return cast(Solidity)((metadata>>(side*2)) & 0b11);
60 	else
61 		return Solidity.transparent; // otherwise non-solid
62 }
63 
64 /// Returns true if chunk has blocks of only single solidity.
65 /// If returns true then solidity has solidity of all blocks.
66 bool hasSingleSolidity(const ushort metadata, out Solidity solidity)
67 {
68 	if (metadata & 0b1_00_00_00_00_00_00) {// if metadata is valid
69 		ubyte solidityBits = (metadata>>CHUNK_SIDE_METADATA_BITS) & 0b111;
70 		solidity = bitsToSolidityTable[solidityBits];
71 		return singleSolidityTable[solidityBits];
72 	} else {
73 		return false; // assume has every solidity.
74 	}
75 }
76 
77 void printChunkMetadata(ushort metadata)
78 {
79 	if (metadata & 0b1_00_00_00_00_00_00) {// if metadata is valid
80 		char[6] sideSolidityChars;
81 		char[3] letters = "TMS";
82 		foreach(side; 0..6) {
83 			Solidity sideSolidity = cast(Solidity)((metadata>>(side*2)) & 0b11);
84 			sideSolidityChars[side] = letters[sideSolidity];
85 		}
86 		ubyte solidityBits = (metadata>>CHUNK_SIDE_METADATA_BITS) & 0b111;
87 		char trans = solidityBits & 1 ? 'T' : ' ';
88 		char semi = solidityBits & 0b10 ? 'M' : ' ';
89 		char solid = solidityBits & 0b100 ? 'S' : ' ';
90 		Solidity singleSolidity;
91 		bool single = hasSingleSolidity(metadata, singleSolidity);
92 		infof("meta [%s%s%s] (%s) {%s, %s}", trans, semi, solid, sideSolidityChars, single, singleSolidity);
93 	} else {
94 		infof("non-valid metadata");
95 	}
96 }
97 
98 // ditto
99 private ubyte calcSolidityBits(Layer)(Layer blockLayer, BlockInfoTable blockInfos)
100 	if (isSomeLayer!Layer)
101 {
102 	if (blockLayer.type == StorageType.uniform) {
103 		return calcSolidityBits(blockLayer.getUniform!BlockId, blockInfos);
104 	} else if (blockLayer.type == StorageType.fullArray) {
105 		BlockId[] blocks = blockLayer.getArray!BlockId;
106 		return calcSolidityBits(blocks, blockInfos);
107 	} else assert(false);
108 }
109 
110 enum ubyte[3] solidity_bits = [0b001, 0b010, 0b100];
111 private ubyte calcSolidityBits(BlockId uniformBlock, BlockInfoTable blockInfos)
112 {
113 	Solidity solidity = blockInfos[uniformBlock].solidity;
114 	return solidity_bits[solidity];
115 }
116 
117 private ubyte calcSolidityBits(BlockId[] blocks, BlockInfoTable blockInfos)
118 {
119 	bool[3] presentSolidities;
120 	foreach(i; 0..CHUNK_SIZE_CUBE) {
121 		Solidity solidity = blockInfos[blocks[i]].solidity;
122 		presentSolidities[solidity] = true;
123 	}
124 	ubyte solidityBits;
125 	ubyte solidityFlag = 1;
126 	foreach(sol; presentSolidities) {
127 		if (sol) solidityBits |= solidityFlag;
128 		solidityFlag <<= 1;
129 	}
130 	return solidityBits;
131 }
132 
133 /// Returns true if chunk has blocks of specified solidity.
134 /// If metadata is invalid then chunk is assumed to have blocks of every solidity.
135 private bool hasSolidity(const ushort metadata, Solidity solidity)
136 {
137 	ubyte solidityBits;
138 	if (metadata & 0b1_00_00_00_00_00_00) {// if metadata is valid
139 		solidityBits = (metadata>>CHUNK_SIDE_METADATA_BITS) & 0b111;
140 	} else {
141 		solidityBits = 0b111; // assume every solidity.
142 	}
143 	return (solidityBits & (1 << solidity)) > 0;
144 }
145 
146 /// Returns true if chunk has blocks only of specified solidity.
147 /// If metadata is invalid then chunk is assumed to have blocks of every solidity, and returns false.
148 private bool hasOnlySolidity(const ushort metadata, Solidity solidity)
149 {
150 	if (metadata & 0b1_00_00_00_00_00_00) {// if metadata is valid
151 		ubyte solidityBits = (metadata>>CHUNK_SIDE_METADATA_BITS) & 0b111;
152 		return solidityBits == (1 << solidity);
153 	} else {
154 		return false; // assume has every solidity.
155 	}
156 }
157 
158 private bool[8] singleSolidityTable = [false, true, true, false, true, false, false, false];
159 private Solidity[8] bitsToSolidityTable = [Solidity.transparent, Solidity.transparent, Solidity.semiTransparent,
160 	Solidity.transparent, Solidity.solid, Solidity.transparent, Solidity.transparent, Solidity.transparent];
161 
162 immutable ushort[3] solidity_metadatas = [0b1_00_00_00_00_00_00, 0b1_01_01_01_01_01_01, 0b1_10_10_10_10_10_10];
163 
164 enum CHUNK_SIDE_METADATA_BITS = 13;
165 enum SOLID_CHUNK_METADATA = cast(ushort) (solidity_metadatas[Solidity.solid] |
166 		solidity_bits[Solidity.solid]<<CHUNK_SIDE_METADATA_BITS);
167 enum TRANSPARENT_CHUNK_METADATA = cast(ushort) (solidity_metadatas[Solidity.transparent] |
168 		solidity_bits[Solidity.transparent]<<CHUNK_SIDE_METADATA_BITS);
169 
170 // calculate info about sides of chunk. 2 bits per side. Value is solidity.
171 private ushort calcChunkSideMetadata(Layer)(Layer blockLayer, BlockInfoTable blockInfos)
172 	if (isSomeLayer!Layer)
173 {
174 	if (blockLayer.type == StorageType.uniform) {
175 		return calcChunkSideMetadata(blockLayer.getUniform!BlockId, blockInfos);
176 	} else if (blockLayer.type == StorageType.fullArray) {
177 		BlockId[] blocks = blockLayer.getArray!BlockId;
178 		return calcChunkSideMetadata(blocks, blockInfos);
179 	} else assert(false);
180 }
181 
182 // ditto
183 private ushort calcChunkSideMetadata(BlockId uniformBlock, BlockInfoTable blockInfos)
184 {
185 	Solidity solidity = blockInfos[uniformBlock].solidity;
186 	// 13th bit == 1 when metadata is present, 12 bits = solidity of 6 chunk sides. 2 bits per side
187 	return solidity_metadatas[solidity];
188 }
189 
190 // ditto
191 private ushort calcChunkSideMetadata(BlockId[] blocks, BlockInfoTable blockInfos)
192 {
193 	ushort flags = 0b1_00_00_00_00_00_00; // all sides are solid
194 	Solidity sideSolidity = Solidity.solid;
195 	foreach(index; 0..CHUNK_SIZE_SQR) // yneg
196 	{
197 		if (sideSolidity > blockInfos[blocks[index]].solidity)
198 		{
199 			--sideSolidity;
200 			if (sideSolidity == Solidity.transparent)
201 				break;
202 		}
203 	}
204 	flags = cast(ushort)(flags | (sideSolidity << (CubeSide.yneg*2)));
205 
206 	sideSolidity = Solidity.solid;
207 	outer_zneg:
208 	foreach(y; 0..CHUNK_SIZE)
209 	foreach(x; 0..CHUNK_SIZE)
210 	{
211 		size_t index = y*CHUNK_SIZE_SQR | x; // zneg
212 		if (sideSolidity > blockInfos[blocks[index]].solidity)
213 		{
214 			--sideSolidity;
215 			if (sideSolidity == Solidity.transparent)
216 				break outer_zneg;
217 		}
218 	}
219 	flags = cast(ushort)(flags | (sideSolidity << (CubeSide.zneg*2)));
220 
221 	sideSolidity = Solidity.solid;
222 	outer_zpos:
223 	foreach(y; 0..CHUNK_SIZE)
224 	foreach(x; 0..CHUNK_SIZE)
225 	{
226 		size_t index = (CHUNK_SIZE-1) * CHUNK_SIZE | y*CHUNK_SIZE_SQR | x; // zpos
227 		if (sideSolidity > blockInfos[blocks[index]].solidity)
228 		{
229 			--sideSolidity;
230 			if (sideSolidity == Solidity.transparent)
231 				break outer_zpos;
232 		}
233 	}
234 	flags = cast(ushort)(flags | (sideSolidity << (CubeSide.zpos*2)));
235 
236 	sideSolidity = Solidity.solid;
237 	outer_xpos:
238 	foreach(y; 0..CHUNK_SIZE)
239 	foreach(z; 0..CHUNK_SIZE)
240 	{
241 		size_t index = z * CHUNK_SIZE | y*CHUNK_SIZE_SQR | (CHUNK_SIZE-1); // xpos
242 		if (sideSolidity > blockInfos[blocks[index]].solidity)
243 		{
244 			--sideSolidity;
245 			if (sideSolidity == Solidity.transparent)
246 				break outer_xpos;
247 		}
248 	}
249 	flags = cast(ushort)(flags | (sideSolidity << (CubeSide.xpos*2)));
250 
251 	sideSolidity = Solidity.solid;
252 	outer_xneg:
253 	foreach(y; 0..CHUNK_SIZE)
254 	foreach(z; 0..CHUNK_SIZE)
255 	{
256 		size_t index = z * CHUNK_SIZE | y*CHUNK_SIZE_SQR; // xneg
257 		if (sideSolidity > blockInfos[blocks[index]].solidity)
258 		{
259 			--sideSolidity;
260 			if (sideSolidity == Solidity.transparent)
261 				break outer_xneg;
262 		}
263 	}
264 	flags = cast(ushort)(flags | (sideSolidity << (CubeSide.xneg*2)));
265 
266 	sideSolidity = Solidity.solid;
267 	foreach(index; CHUNK_SIZE_CUBE-CHUNK_SIZE_SQR..CHUNK_SIZE_CUBE) // ypos
268 	{
269 		if (sideSolidity > blockInfos[blocks[index]].solidity)
270 		{
271 			--sideSolidity;
272 			if (sideSolidity == Solidity.transparent)
273 				break;
274 		}
275 	}
276 	flags = cast(ushort)(flags | (sideSolidity << (CubeSide.ypos*2)));
277 
278 	return flags;
279 }
280 
281 
282 /*
283 void iterateSides()
284 {
285 	foreach(index; 0..CHUNK_SIZE_SQR) // yneg
286 
287 	{// zneg
288 		ubyte z = 0;
289 		foreach(y; 0..CHUNK_SIZE)
290 			foreach(x; 0..CHUNK_SIZE)
291 				index = z * CHUNK_SIZE | y*CHUNK_SIZE_SQR | x;
292 	}
293 
294 	{// zpos
295 		ubyte z = CHUNK_SIZE-1;
296 		foreach(y; 0..CHUNK_SIZE)
297 			foreach(x; 0..CHUNK_SIZE)
298 				index = z * CHUNK_SIZE | y*CHUNK_SIZE_SQR | x;
299 	}
300 
301 	{// xpos
302 		ubyte x = CHUNK_SIZE-1;
303 		foreach(y; 0..CHUNK_SIZE)
304 			foreach(z; 0..CHUNK_SIZE)
305 				index = z * CHUNK_SIZE | y*CHUNK_SIZE_SQR | x;
306 	}
307 
308 	{// xneg
309 		ubyte x = 0;
310 		foreach(y; 0..CHUNK_SIZE)
311 			foreach(z; 0..CHUNK_SIZE)
312 				index = z * CHUNK_SIZE | y*CHUNK_SIZE_SQR | x;
313 	}
314 
315 	foreach(index; CHUNK_SIZE_CUBE-CHUNK_SIZE_SQR..CHUNK_SIZE_CUBE) // ypos
316 }
317 */