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.utils; 7 8 import voxelman.log; 9 10 import voxelman.container.buffer; 11 import voxelman.math; 12 import voxelman.geometry.cube; 13 import voxelman.core.config; 14 import voxelman.world.storage; 15 import voxelman.utils.mapping; 16 import voxelman.world.block; 17 import voxelman.world.mesh.chunkmesh; 18 19 20 struct ChunkAndBlockAdjacent 21 { 22 ubyte[27] chunks; 23 ubyte[3][27] blocks; 24 } 25 26 struct ChunkAndBlockAt 27 { 28 ubyte chunk; 29 // position of block in chunk 30 ubyte bx, by, bz; 31 } 32 33 // 0-5 sides, or 6 if center 34 ChunkAndBlockAt chunkAndBlockAt6(int x, int y, int z) 35 { 36 ubyte bx = cast(ubyte)x; 37 ubyte by = cast(ubyte)y; 38 ubyte bz = cast(ubyte)z; 39 if(x == -1) return ChunkAndBlockAt(CubeSide.xneg, CHUNK_SIZE-1, by, bz); 40 else if(x == CHUNK_SIZE) return ChunkAndBlockAt(CubeSide.xpos, 0, by, bz); 41 42 if(y == -1) return ChunkAndBlockAt(CubeSide.yneg, bx, CHUNK_SIZE-1, bz); 43 else if(y == CHUNK_SIZE) return ChunkAndBlockAt(CubeSide.ypos, bx, 0, bz); 44 45 if(z == -1) return ChunkAndBlockAt(CubeSide.zneg, bx, by, CHUNK_SIZE-1); 46 else if(z == CHUNK_SIZE) return ChunkAndBlockAt(CubeSide.zpos, bx, by, 0); 47 48 return ChunkAndBlockAt(26, bx, by, bz); 49 } 50 51 // convert -1..33 -> 0..34 to use as index 52 ubyte[34] position_in_target_chunk = [CHUNK_SIZE-1, // CHUNK_SIZE-1 is in adjacent chunk 53 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, 54 17,18,19,20,21,22,23,24,25,26,27,28,29,30,31, 0]; // 0 is in adjacent chunk 55 // 0 chunk in neg direction, 1 this chunk, 1 pos dir 56 ubyte[34] target_chunk = 57 [0, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 2]; 58 59 ChunkAndBlockAt chunkAndBlockAt27(int x, int y, int z) 60 { 61 ubyte bx = position_in_target_chunk[x+1]; 62 ubyte by = position_in_target_chunk[y+1]; 63 ubyte bz = position_in_target_chunk[z+1]; 64 65 ubyte cx = target_chunk[x+1]; 66 ubyte cy = target_chunk[y+1]; 67 ubyte cz = target_chunk[z+1]; 68 69 ubyte chunk_index = cast(ubyte)(cx + cz * 3 + cy * 9); 70 71 return ChunkAndBlockAt(chunk_index, bx, by, bz); 72 } 73 74 CubeSide sideFromNormal(ivec3 normal) 75 { 76 if (normal.x == 1) 77 return CubeSide.xpos; 78 else if (normal.x == -1) 79 return CubeSide.xneg; 80 81 if (normal.y == 1) 82 return CubeSide.ypos; 83 else if (normal.y == -1) 84 return CubeSide.yneg; 85 86 if (normal.z == 1) 87 return CubeSide.zpos; 88 else if (normal.z == -1) 89 return CubeSide.zneg; 90 91 return CubeSide.zneg; 92 } 93 94 struct MeshVertex2 95 { 96 align(4): 97 float x, y, z; 98 ubyte[3] color; 99 } 100 101 ushort packColor(ubvec3 c) { 102 return (c.r>>3) | (c.g&31) << 5 | (c.b&31) << 10; 103 } 104 ushort packColor(ubyte r, ubyte g, ubyte b) { 105 return (r>>3) | (g&31) << 5 | (b&31) << 10; 106 } 107 108 alias BlockUpdateHandler = void delegate(BlockWorldPos bwp); 109 struct BlockMeshingData 110 { 111 Buffer!MeshVertex* buffer; 112 ubyte[4] delegate(ushort blockIndex, CubeSide side) occlusionHandler; 113 ubvec3 color; 114 ubvec3 chunkPos; 115 ubyte sides; 116 ushort blockIndex; 117 BlockMetadata metadata; 118 } 119 alias MeshHandler = void function(BlockMeshingData); 120 alias ShapeMetaHandler = BlockShape function(BlockMetadata); 121 alias RotationHandler = void function(ref BlockMetadata, ubyte, CubeSide); 122 void makeNullMesh(BlockMeshingData) {} 123 124 alias SideSolidityHandler = Solidity function(CubeSide); 125 Solidity transparentSideSolidity(CubeSide) { return Solidity.transparent; } 126 Solidity semitransparentSideSolidity(CubeSide) { return Solidity.semiTransparent; } 127 Solidity solidSideSolidity(CubeSide) { return Solidity.solid; } 128 129 // solidity number increases with solidity 130 enum Solidity : ubyte 131 { 132 transparent, 133 semiTransparent, 134 solid, 135 } 136 137 bool isMoreSolidThan(Solidity first, Solidity second) 138 { 139 return first > second; 140 } 141 142 struct BlockInfo 143 { 144 string name; 145 MeshHandler meshHandler = &makeNullMesh; 146 ubvec3 color; 147 bool isVisible = true; 148 Solidity solidity = Solidity.solid; 149 BlockShape shape = fullShape; 150 ShapeMetaHandler shapeMetaHandler; 151 RotationHandler rotationHandler; 152 bool shapeDependsOnMeta = false; 153 bool meshDependOnMeta = false; 154 size_t id; 155 } 156 157 BlockInfo entityBlock = BlockInfo("Entity", &makeColoredFullBlockMesh); 158 struct BlockInfoTable 159 { 160 immutable(BlockInfo)[] blockInfos; 161 SideIntersectionTable sideTable; 162 163 size_t length() {return blockInfos.length; } 164 165 BlockInfo opIndex(BlockId blockId) { 166 if (blockId >= blockInfos.length) 167 return entityBlock; 168 return blockInfos[blockId]; 169 } 170 } 171 172 struct SeparatedBlockInfoTable 173 { 174 this(BlockInfoTable infoTable) 175 { 176 sideTable = infoTable.sideTable; 177 shape.length = infoTable.length; 178 corners.length = infoTable.length; 179 hasGeometry.length = infoTable.length; 180 hasInternalGeometry.length = infoTable.length; 181 sideMasks.length = infoTable.length; 182 color.length = infoTable.length; 183 meshHandler.length = infoTable.length; 184 shapeMetaHandler.length = infoTable.length; 185 shapeDependsOnMeta.length = infoTable.length; 186 meshDependOnMeta.length = infoTable.length; 187 188 foreach(i, binfo; infoTable.blockInfos) 189 { 190 shape[i] = binfo.shape; 191 corners[i] = binfo.shape.corners; 192 hasGeometry[i] = binfo.shape.hasGeometry; 193 hasInternalGeometry[i] = binfo.shape.hasInternalGeometry; 194 sideMasks[i] = binfo.shape.sideMasks; 195 color[i] = binfo.color; 196 meshHandler[i] = binfo.meshHandler; 197 shapeMetaHandler[i] = binfo.shapeMetaHandler; 198 shapeDependsOnMeta[i] = binfo.shapeDependsOnMeta; 199 meshDependOnMeta[i] = binfo.meshDependOnMeta; 200 } 201 202 blockInfos = infoTable.blockInfos; 203 } 204 205 immutable(BlockInfo)[] blockInfos; 206 SideIntersectionTable sideTable; 207 BlockShape[] shape; 208 ubyte[] corners; 209 bool[] hasGeometry; 210 bool[] hasInternalGeometry; 211 bool[] shapeDependsOnMeta; 212 bool[] meshDependOnMeta; 213 ShapeSideMask[6][] sideMasks; 214 ubvec3[] color; 215 MeshHandler[] meshHandler; 216 ShapeMetaHandler[] shapeMetaHandler; 217 } 218 219 /// Returned when registering block. 220 /// Use this to set block properties. 221 struct BlockInfoSetter 222 { 223 private Mapping!(BlockInfo)* mapping; 224 private size_t blockId; 225 private ref BlockInfo info() {return (*mapping)[blockId]; } 226 227 ref BlockInfoSetter meshHandler(MeshHandler val) { info.meshHandler = val; return this; } 228 ref BlockInfoSetter color(ubyte[3] color ...) { info.color = ubvec3(color); return this; } 229 ref BlockInfoSetter colorHex(uint hex) { info.color = ubvec3((hex>>16)&0xFF,(hex>>8)&0xFF,hex&0xFF); return this; } 230 ref BlockInfoSetter isVisible(bool val) { info.isVisible = val; return this; } 231 ref BlockInfoSetter solidity(Solidity val) { info.solidity = val; return this; } 232 ref BlockInfoSetter blockShape(BlockShape val) { info.shape = val; return this; } 233 ref BlockInfoSetter shapeMetaHandler(ShapeMetaHandler val) { 234 info.shapeMetaHandler = val; 235 info.shapeDependsOnMeta = true; 236 return this; 237 } 238 //ref BlockInfoSetter shapeDependsOnMeta(bool val) { info.shapeDependsOnMeta = val; return this; } 239 ref BlockInfoSetter meshDependOnMeta(bool val) { info.meshDependOnMeta = val; return this; } 240 ref BlockInfoSetter rotationHandler(RotationHandler val) { info.rotationHandler = val; return this; } 241 } 242 243 import voxelman.world.mesh.blockmeshers.full; 244 import voxelman.world.mesh.blockmeshers.slope; 245 246 void regBaseBlocks(BlockInfoSetter delegate(string name) regBlock) 247 { 248 regBlock("unknown").color(0,0,0).isVisible(false).solidity(Solidity.solid).meshHandler(&makeNullMesh).blockShape(unknownShape); 249 regBlock("air").color(0,0,0).isVisible(false).solidity(Solidity.transparent).meshHandler(&makeNullMesh).blockShape(emptyShape); 250 regBlock("grass").colorHex(0x01A611).meshHandler(&makeColoredFullBlockMesh); 251 regBlock("dirt").colorHex(0x835929).meshHandler(&makeColoredFullBlockMesh); 252 regBlock("stone").colorHex(0x8B8D7A).meshHandler(&makeColoredFullBlockMesh); 253 regBlock("sand").colorHex(0xA68117).meshHandler(&makeColoredFullBlockMesh); 254 regBlock("water").colorHex(0x0055AA).meshHandler(&makeColoredFullBlockMesh).solidity(Solidity.semiTransparent).blockShape(waterShape); 255 regBlock("lava").colorHex(0xFF6920).meshHandler(&makeColoredFullBlockMesh); 256 regBlock("snow").colorHex(0xDBECF6).meshHandler(&makeColoredFullBlockMesh); 257 regBlock("slope").colorHex(0x857FFF).meshHandler(&makeColoredSlopeBlockMesh).shapeMetaHandler(&slopeShapeFromMeta) 258 .meshDependOnMeta(true).rotationHandler(&slopeRotationHandler); 259 } 260 261 void setSideTable(ref SideIntersectionTable sideTable) 262 { 263 sideTable.set(ShapeSideMask.full, ShapeSideMask.empty); 264 sideTable.set(ShapeSideMask.water, ShapeSideMask.empty); 265 sideTable.set(ShapeSideMask.full, ShapeSideMask.water); 266 267 sideTable.set(ShapeSideMask.water, ShapeSideMask.slope0); 268 sideTable.set(ShapeSideMask.full, ShapeSideMask.slope0); 269 sideTable.set(ShapeSideMask.water, ShapeSideMask.slope1); 270 sideTable.set(ShapeSideMask.full, ShapeSideMask.slope1); 271 sideTable.set(ShapeSideMask.water, ShapeSideMask.slope2); 272 sideTable.set(ShapeSideMask.full, ShapeSideMask.slope2); 273 sideTable.set(ShapeSideMask.water, ShapeSideMask.slope3); 274 sideTable.set(ShapeSideMask.full, ShapeSideMask.slope3); 275 276 sideTable.set(ShapeSideMask.slope0, ShapeSideMask.slope0); 277 sideTable.set(ShapeSideMask.slope0, ShapeSideMask.slope1); 278 sideTable.set(ShapeSideMask.slope0, ShapeSideMask.slope2); 279 sideTable.set(ShapeSideMask.slope0, ShapeSideMask.empty); 280 sideTable.set(ShapeSideMask.slope0, ShapeSideMask.water); 281 282 sideTable.set(ShapeSideMask.slope1, ShapeSideMask.slope0); 283 sideTable.set(ShapeSideMask.slope1, ShapeSideMask.slope1); 284 sideTable.set(ShapeSideMask.slope1, ShapeSideMask.slope3); 285 sideTable.set(ShapeSideMask.slope1, ShapeSideMask.empty); 286 sideTable.set(ShapeSideMask.slope1, ShapeSideMask.water); 287 288 sideTable.set(ShapeSideMask.slope2, ShapeSideMask.slope0); 289 sideTable.set(ShapeSideMask.slope2, ShapeSideMask.slope2); 290 sideTable.set(ShapeSideMask.slope2, ShapeSideMask.slope3); 291 sideTable.set(ShapeSideMask.slope2, ShapeSideMask.empty); 292 sideTable.set(ShapeSideMask.slope2, ShapeSideMask.water); 293 294 sideTable.set(ShapeSideMask.slope3, ShapeSideMask.slope1); 295 sideTable.set(ShapeSideMask.slope3, ShapeSideMask.slope2); 296 sideTable.set(ShapeSideMask.slope3, ShapeSideMask.slope3); 297 sideTable.set(ShapeSideMask.slope3, ShapeSideMask.empty); 298 sideTable.set(ShapeSideMask.slope3, ShapeSideMask.water); 299 } 300 301 void slopeRotationHandler(ref BlockMetadata meta, ubyte rotation, CubeSide side) 302 { 303 import voxelman.world.mesh.tables.slope : slopeSideRotations; 304 meta = slopeSideRotations[side][rotation]; 305 }