1 /** 2 Copyright: Copyright (c) 2013-2017 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.cube; 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 */