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