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.core.meshgen; 7 8 import std.experimental.logger; 9 import std.conv : to; 10 import core.exception : Throwable; 11 12 import voxelman.container.buffer; 13 import voxelman.math; 14 import voxelman.geometry.cube; 15 16 import voxelman.block.plugin; 17 import voxelman.blockentity.plugin; 18 19 import voxelman.block.utils; 20 import voxelman.core.chunkmesh; 21 import voxelman.core.config; 22 import voxelman.utils.worker; 23 import voxelman.world.storage.chunk; 24 import voxelman.world.storage.coordinates; 25 26 27 enum MeshGenTaskType : ubyte 28 { 29 genMesh, 30 unloadMesh 31 } 32 33 struct MeshGenTaskHeader 34 { 35 MeshGenTaskType type; 36 size_t meshGroupId; 37 ChunkWorldPos cwp; 38 } 39 40 //version = DBG_OUT; 41 void meshWorkerThread(shared(Worker)* workerInfo, BlockInfoTable blockInfos, BlockEntityInfoTable beInfos) 42 { 43 try 44 { 45 while (workerInfo.needsToRun) 46 { 47 workerInfo.waitForNotify(); 48 49 // receive 50 // MeshGenTaskHeader taskHeader; 51 // ChunkLayerItem[7] blockLayers; 52 // ChunkLayerItem[7] entityLayers; 53 // or 54 // MeshGenTaskHeader taskHeader; 55 // 56 // send 57 // MeshGenTaskHeader taskHeader; 58 // MeshVertex[][2] meshes; 59 // uint[7] blockTimestamps; 60 // uint[7] entityTimestamps; 61 // or 62 // MeshGenTaskHeader taskHeader; 63 if (!workerInfo.taskQueue.empty) 64 { 65 auto taskHeader = workerInfo.taskQueue.popItem!MeshGenTaskHeader(); 66 67 if (taskHeader.type == MeshGenTaskType.genMesh) 68 { 69 // mesh task. 70 ChunkLayerItem[7] blockLayers = workerInfo.taskQueue.popItem!(ChunkLayerItem[7])(); 71 ChunkLayerItem[7] entityLayers = workerInfo.taskQueue.popItem!(ChunkLayerItem[7])(); 72 73 MeshVertex[][2] meshes = chunkMeshWorker(blockLayers, entityLayers, blockInfos, beInfos); 74 75 uint[7] blockTimestamps; 76 uint[7] entityTimestamps; 77 foreach(i; 0..7) blockTimestamps[i] = blockLayers[i].timestamp; 78 foreach(i; 0..7) entityTimestamps[i] = entityLayers[i].timestamp; 79 80 workerInfo.resultQueue.startMessage(); 81 workerInfo.resultQueue.pushMessagePart(taskHeader); 82 workerInfo.resultQueue.pushMessagePart(meshes); 83 workerInfo.resultQueue.pushMessagePart(blockTimestamps); 84 workerInfo.resultQueue.pushMessagePart(entityTimestamps); 85 workerInfo.resultQueue.endMessage(); 86 } 87 else 88 { 89 // remove mesh task. Resend it to main thread. 90 workerInfo.resultQueue.pushItem(taskHeader); 91 } 92 } 93 } 94 } 95 catch(Throwable t) 96 { 97 infof("%s from mesh worker", t.to!string); 98 throw t; 99 } 100 version(DBG_OUT)infof("Mesh worker stopped"); 101 } 102 103 MeshVertex[][2] chunkMeshWorker(ChunkLayerItem[7] blockLayers, 104 ChunkLayerItem[7] entityLayers, BlockInfoTable blockInfos, BlockEntityInfoTable beInfos) 105 { 106 Buffer!MeshVertex[3] geometry; // 2 - solid, 1 - semiTransparent 107 108 foreach (layer; blockLayers) 109 assert(layer.type != StorageType.compressedArray, "[MESHING] Data needs to be uncompressed"); 110 111 BlockEntityMap[7] maps; 112 foreach (i, layer; entityLayers) maps[i] = getHashMapFromLayer(layer); 113 114 BlockEntityData getBlockEntity(ushort blockIndex, BlockEntityMap map) { 115 ulong* entity = blockIndex in map; 116 if (entity is null) return BlockEntityData.init; 117 return BlockEntityData(*entity); 118 } 119 120 Solidity solidity(int tx, int ty, int tz, CubeSide side) 121 { 122 ChunkAndBlockAt chAndBlock = chunkAndBlockAt(tx, ty, tz); 123 BlockId blockId = blockLayers[chAndBlock.chunk].getBlockId( 124 chAndBlock.blockX, chAndBlock.blockY, chAndBlock.blockZ); 125 126 if (isBlockEntity(blockId)) { 127 ushort entityBlockIndex = blockIndexFromBlockId(blockId); 128 BlockEntityData data = getBlockEntity(entityBlockIndex, maps[chAndBlock.chunk]); 129 auto entityInfo = beInfos[data.id]; 130 131 auto entityChunkPos = BlockChunkPos(entityBlockIndex); 132 133 ivec3 blockChunkPos = ivec3(chAndBlock.blockX, chAndBlock.blockY, chAndBlock.blockZ); 134 ivec3 blockEntityPos = blockChunkPos - entityChunkPos.vector; 135 136 return entityInfo.sideSolidity(side, blockChunkPos, blockEntityPos, data); 137 } else { 138 return blockInfos[blockId].solidity; 139 } 140 } 141 142 ubyte checkSideSolidities(Solidity curSolidity, ubyte bx, ubyte by, ubyte bz) 143 { 144 ubyte sides = 0; 145 ubyte flag = 1; 146 foreach(ubyte side; 0..6) { 147 byte[3] offset = sideOffsets[side]; // Offset to adjacent block 148 if(curSolidity > solidity(bx+offset[0], by+offset[1], bz+offset[2], oppSide[side])) { 149 sides |= flag; 150 } 151 flag <<= 1; 152 } 153 return sides; 154 } 155 156 void meshBlock(BlockId blockId, ushort blockIndex, Solidity curSolidity) 157 { 158 ubyte bx = blockIndex & CHUNK_SIZE_BITS; 159 ubyte by = (blockIndex / CHUNK_SIZE_SQR) & CHUNK_SIZE_BITS; 160 ubyte bz = (blockIndex / CHUNK_SIZE) & CHUNK_SIZE_BITS; 161 162 // Bit flags of sides to render 163 ubyte sides = checkSideSolidities(curSolidity, bx, by, bz); 164 165 if (isBlockEntity(blockId)) 166 { 167 ushort entityBlockIndex = blockIndexFromBlockId(blockId); 168 BlockEntityData data = getBlockEntity(entityBlockIndex, maps[6]); 169 170 // entity chunk pos 171 auto entityChunkPos = BlockChunkPos(entityBlockIndex); 172 173 //ivec3 worldPos; 174 ivec3 blockChunkPos = ivec3(bx, by, bz); 175 ivec3 blockEntityPos = blockChunkPos - entityChunkPos.vector; 176 177 auto entityInfo = beInfos[data.id]; 178 179 entityInfo.meshHandler( 180 geometry[], 181 data, 182 entityInfo.color, 183 sides, 184 //worldPos, 185 blockChunkPos, 186 blockEntityPos); 187 } 188 else 189 { 190 blockInfos[blockId].meshHandler(geometry[curSolidity], blockInfos[blockId].color, bx, by, bz, sides); 191 } 192 } 193 194 if (blockLayers[6].isUniform) 195 { 196 BlockId blockId = blockLayers[6].getUniform!BlockId; 197 Meshhandler meshHandler = blockInfos[blockId].meshHandler; 198 ubyte[3] color = blockInfos[blockId].color; 199 Solidity curSolidity = blockInfos[blockId].solidity; 200 201 if (curSolidity != Solidity.transparent) 202 { 203 foreach (ushort index; 0..CHUNK_SIZE_CUBE) 204 { 205 meshBlock(blockId, index, curSolidity); 206 } 207 } 208 } 209 else 210 { 211 auto blocks = blockLayers[6].getArray!BlockId(); 212 assert(blocks.length == CHUNK_SIZE_CUBE); 213 foreach (ushort index, BlockId blockId; blocks) 214 { 215 if (blockInfos[blockId].isVisible) 216 { 217 auto curSolidity = blockInfos[blockId].solidity; 218 if (curSolidity == Solidity.transparent) 219 continue; 220 221 meshBlock(blockId, index, curSolidity); 222 } 223 } 224 } 225 226 MeshVertex[][2] meshes; 227 meshes[0] = geometry[2].data; // solid geometry 228 meshes[1] = geometry[1].data; // semi-transparent geometry 229 230 // Add root to data. 231 // Data can be collected by GC if no-one is referencing it. 232 // It is needed to pass array trough shared queue. 233 // Root is removed inside ChunkMeshMan 234 import core.memory : GC; 235 if (meshes[0]) GC.addRoot(meshes[0].ptr); // TODO remove when moved to non-GC allocator 236 if (meshes[1]) GC.addRoot(meshes[1].ptr); // 237 238 return meshes; 239 }