#include "VulkanRenderSession.hpp" #include "Client/Abstract.hpp" #include "Client/Vulkan/Abstract.hpp" #include "Client/Vulkan/Vulkan.hpp" #include "Common/Abstract.hpp" #include "TOSLib.hpp" #include "assets.hpp" #include "glm/ext/matrix_transform.hpp" #include "glm/ext/scalar_constants.hpp" #include "glm/matrix.hpp" #include "glm/trigonometric.hpp" #include #include #include #include #include #include #include #include #include #include #include #include namespace std { template<> struct hash { size_t operator()(const LV::Client::VK::NodeVertexStatic& v) const { const uint32_t* ptr = reinterpret_cast(&v); size_t h1 = std::hash{}(ptr[0]); size_t h2 = std::hash{}(ptr[1]); size_t h3 = std::hash{}(ptr[2]); return h1 ^ (h2 << 1) ^ (h3 << 2); } }; } namespace LV::Client::VK { void ChunkMeshGenerator::changeThreadsCount(uint8_t threads) { Sync.NeedShutdown = true; std::unique_lock lock(Sync.Mutex); Sync.CV_CountInRun.wait(lock, [&]() { return Sync.CountInRun == 0; }); for(std::thread& thr : Threads) thr.join(); Sync.NeedShutdown = false; Threads.resize(threads); for(int iter = 0; iter < threads; iter++) Threads[iter] = std::thread(&ChunkMeshGenerator::run, this, iter); Sync.CV_CountInRun.wait(lock, [&]() { return Sync.CountInRun == Threads.size() || Sync.NeedShutdown; }); if(Sync.NeedShutdown) MAKE_ERROR("Ошибка обработчика вершин чанков"); } void ChunkMeshGenerator::run(uint8_t id) { Logger LOG = "ChunkMeshGenerator<"+std::to_string(id)+'>'; { std::unique_lock lock(Sync.Mutex); Sync.CountInRun += 1; Sync.CV_CountInRun.notify_all(); } LOG.debug() << "Старт потока верширования чанков"; int timeWait = 1; try { while(!Sync.NeedShutdown) { if(Sync.Stop) { // Мир клиента начинает обрабатывать такты std::unique_lock lock(Sync.Mutex); if(Sync.Stop) { Sync.CountInRun -= 1; Sync.CV_CountInRun.notify_all(); Sync.CV_CountInRun.wait(lock, [&](){ return !Sync.Stop; }); Sync.CountInRun += 1; } } // Если нет входных запросов - ожидаем if(Input.get_read().empty()) { std::this_thread::sleep_for(std::chrono::milliseconds(timeWait)); if(++timeWait > 20) timeWait = 20; continue; } timeWait = 0; WorldId_t wId; Pos::GlobalChunk pos; uint32_t requestId; { auto lock = Input.lock(); if(lock->empty()) continue; std::tuple v = lock->front(); wId = std::get<0>(v); pos = std::get<1>(v); requestId = std::get<2>(v); lock->pop(); } ChunkObj_t result; result.RequestId = requestId; result.WId = wId; result.Pos = pos; const std::array* chunk; const std::vector* voxels; // Если на позиции полная нода, то она перекрывает стороны соседей uint8_t fullNodes[18][18][18]; // Профиль, который используется если на стороне клиента отсутствует нужных профиль DefNode_t defaultProfileNode; // Кеш запросов профилей нод std::unordered_map profilesNodeCache; auto getNodeProfile = [&](DefNodeId id) -> const DefNode_t* { auto iterCache = profilesNodeCache.find(id); if(iterCache == profilesNodeCache.end()) { // Промах кеша auto iterSS = SS->Profiles.DefNode.find(id); if(iterSS != SS->Profiles.DefNode.end()) { return (profilesNodeCache[id] = &iterSS->second); } else { // Профиль отсутствует на клиенте return (profilesNodeCache[id] = &defaultProfileNode); } } else { return iterCache->second; } }; // Воксели пока не рендерим if(auto iterWorld = SS->Content.Worlds.find(wId); iterWorld != SS->Content.Worlds.end()) { Pos::GlobalRegion rPos = pos >> 2; if(auto iterRegion = iterWorld->second.Regions.find(rPos); iterRegion != iterWorld->second.Regions.end()) { auto& chunkPtr = iterRegion->second.Chunks[Pos::bvec4u(pos & 0x3).pack()]; chunk = &chunkPtr.Nodes; voxels = &chunkPtr.Voxels; } else goto end; // Собрать чанки с каждой стороны для face culling const std::array* chunks[6] = {0}; for(int var = 0; var < 6; var++) { Pos::GlobalChunk chunkPos = pos; if(var == 0) chunkPos += Pos::GlobalChunk(1, 0, 0); else if(var == 1) chunkPos += Pos::GlobalChunk(-1, 0, 0); else if(var == 2) chunkPos += Pos::GlobalChunk(0, 1, 0); else if(var == 3) chunkPos += Pos::GlobalChunk(0, -1, 0); else if(var == 4) chunkPos += Pos::GlobalChunk(0, 0, 1); else if(var == 5) chunkPos += Pos::GlobalChunk(0, 0, -1); rPos = chunkPos >> 2; if(auto iterRegion = iterWorld->second.Regions.find(rPos); iterRegion != iterWorld->second.Regions.end()) { auto& chunkPtr = iterRegion->second.Chunks[Pos::bvec4u(chunkPos & 0x3).pack()]; chunks[var] = &chunkPtr.Nodes; } } std::fill(((uint8_t*) fullNodes), ((uint8_t*) fullNodes)+18*18*18, 0); std::unordered_map nodeFullCuboidCache; auto nodeIsFull = [&](Node node) -> bool { auto iterCache = nodeFullCuboidCache.find(node.Data); if(iterCache == nodeFullCuboidCache.end()) { const DefNode_t* profile = getNodeProfile(node.NodeId); if(profile->TexId != 0) { return (nodeFullCuboidCache[node.Data] = true); } return (nodeFullCuboidCache[node.Data] = false); } else { return iterCache->second; } }; { const Node* n = chunk->data(); for(int z = 0; z < 16; z++) for(int y = 0; y < 16; y++) for(int x = 0; x < 16; x++) { fullNodes[x+1][y+1][z+1] = (uint8_t) nodeIsFull(n[x+y*16+z*16*16]); } } if(chunks[0]) { const Node* n = chunks[0]->data(); for(int z = 0; z < 16; z++) for(int y = 0; y < 16; y++) { fullNodes[17][y+1][z+1] = (uint8_t) nodeIsFull(n[y*16+z*16*16]); } } else { for(int z = 0; z < 16; z++) for(int y = 0; y < 16; y++) fullNodes[17][y+1][z+1] = 0; } if(chunks[1]) { const Node* n = chunks[1]->data(); for(int z = 0; z < 16; z++) for(int y = 0; y < 16; y++) { fullNodes[0][y+1][z+1] = (uint8_t) nodeIsFull(n[15+y*16+z*16*16]); } } else { for(int z = 0; z < 16; z++) for(int y = 0; y < 16; y++) fullNodes[0][y+1][z+1] = 0; } if(chunks[2]) { const Node* n = chunks[2]->data(); for(int z = 0; z < 16; z++) for(int x = 0; x < 16; x++) { fullNodes[x+1][17][z+1] = (uint8_t) nodeIsFull(n[x+0+z*16*16]); } } else { for(int z = 0; z < 16; z++) for(int x = 0; x < 16; x++) fullNodes[x+1][17][z+1] = 0; } if(chunks[3]) { const Node* n = chunks[3]->data(); for(int z = 0; z < 16; z++) for(int x = 0; x < 16; x++) { fullNodes[x+1][0][z+1] = (uint8_t) nodeIsFull(n[x+15*16+z*16*16]); } } else { for(int z = 0; z < 16; z++) for(int x = 0; x < 16; x++) fullNodes[x+1][0][z+1] = 0; } if(chunks[4]) { const Node* n = chunks[4]->data(); for(int y = 0; y < 16; y++) for(int x = 0; x < 16; x++) { fullNodes[x+1][y+1][17] = (uint8_t) nodeIsFull(n[x+y*16+0]); } } else { for(int y = 0; y < 16; y++) for(int x = 0; x < 16; x++) fullNodes[x+1][y+1][17] = 0; } if(chunks[5]) { const Node* n = chunks[5]->data(); for(int y = 0; y < 16; y++) for(int x = 0; x < 16; x++) { fullNodes[x+1][y+1][0] = (uint8_t) nodeIsFull(n[x+y*16+15*16*16]); } } else { for(int y = 0; y < 16; y++) for(int x = 0; x < 16; x++) fullNodes[x+0][y+1][0] = 0; } } else goto end; { result.VoxelDefines.reserve(voxels->size()); for(const VoxelCube& cube : *voxels) result.VoxelDefines.push_back(cube.VoxelId); std::sort(result.VoxelDefines.begin(), result.VoxelDefines.end()); auto eraseIter = std::unique(result.VoxelDefines.begin(), result.VoxelDefines.end()); result.VoxelDefines.erase(eraseIter, result.VoxelDefines.end()); result.VoxelDefines.shrink_to_fit(); } { result.NodeDefines.reserve(16*16*16); for(int iter = 0; iter < 16*16*16; iter++) result.NodeDefines.push_back((*chunk)[iter].NodeId); std::sort(result.NodeDefines.begin(), result.NodeDefines.end()); auto eraseIter = std::unique(result.NodeDefines.begin(), result.NodeDefines.end()); result.NodeDefines.erase(eraseIter, result.NodeDefines.end()); result.NodeDefines.shrink_to_fit(); } // Генерация вершин вокселей { } // Генерация вершин нод { NodeVertexStatic v; std::memset(&v, 0, sizeof(v)); static std::atomic debugMeshWarnCount = 0; const bool debugMeshEnabled = debugMeshWarnCount.load() < 16; std::array expectedColumnX = {}; std::array expectedColumnY = {}; std::array expectedColumnZ = {}; std::array generatedColumnX = {}; std::array generatedColumnY = {}; std::array generatedColumnZ = {}; struct ModelCacheEntry { std::vector>>>> Routes; }; std::unordered_map modelCache; std::unordered_map baseTextureCache; std::vector metaStatesInfo; { NodeStateInfo info; info.Name = "meta"; info.Variations = 256; metaStatesInfo.push_back(std::move(info)); } auto isFaceCovered = [&](EnumFace face, int covered) -> bool { switch(face) { case EnumFace::Up: return covered & (1 << 2); case EnumFace::Down: return covered & (1 << 3); case EnumFace::East: return covered & (1 << 0); case EnumFace::West: return covered & (1 << 1); case EnumFace::South: return covered & (1 << 4); case EnumFace::North: return covered & (1 << 5); default: return false; } }; auto pickVariant = [&](const std::vector>>>& variants, uint32_t seed) -> const std::unordered_map>* { if(variants.empty()) return nullptr; float total = 0.0f; for(const auto& entry : variants) total += std::max(0.0f, entry.first); if(total <= 0.0f) return &variants.front().second; float r = (seed % 10000u) / 10000.0f * total; float accum = 0.0f; for(const auto& entry : variants) { accum += std::max(0.0f, entry.first); if(r <= accum) return &entry.second; } return &variants.back().second; }; auto appendModel = [&](const std::unordered_map>& faces, int covered, int x, int y, int z) { for(const auto& [face, verts] : faces) { if(face != EnumFace::None && isFaceCovered(face, covered)) continue; for(const NodeVertexStatic& baseVert : verts) { NodeVertexStatic vert = baseVert; vert.FX = uint32_t(vert.FX + x * 64); vert.FY = uint32_t(vert.FY + y * 64); vert.FZ = uint32_t(vert.FZ + z * 64); result.NodeVertexs.push_back(vert); } } }; // Сбор вершин for(int z = 0; z < 16; z++) for(int y = 0; y < 16; y++) for(int x = 0; x < 16; x++) { const size_t vertexStart = result.NodeVertexs.size(); int fullCovered = 0; fullCovered |= fullNodes[x+1+1][y+1][z+1]; fullCovered |= fullNodes[x+1-1][y+1][z+1] << 1; fullCovered |= fullNodes[x+1][y+1+1][z+1] << 2; fullCovered |= fullNodes[x+1][y+1-1][z+1] << 3; fullCovered |= fullNodes[x+1][y+1][z+1+1] << 4; fullCovered |= fullNodes[x+1][y+1][z+1-1] << 5; if(fullCovered == 0b111111) continue; const Node& nodeData = (*chunk)[x+y*16+z*16*16]; const DefNode_t* node = getNodeProfile(nodeData.NodeId); if(debugMeshEnabled) { const bool hasRenderable = (node->NodestateId != 0) || (node->TexId != 0); if(hasRenderable && fullCovered != 0b111111) { expectedColumnX[x] = 1; expectedColumnY[y] = 1; expectedColumnZ[z] = 1; } } bool usedModel = false; if(NSP && (node->NodestateId != 0 || NSP->hasNodestate(node->NodestateId))) { auto iterCache = modelCache.find(nodeData.Data); if(iterCache == modelCache.end()) { std::unordered_map states; states.emplace("meta", nodeData.Meta); ModelCacheEntry entry; entry.Routes = NSP->getModelsForNode(node->NodestateId, metaStatesInfo, states); iterCache = modelCache.emplace(nodeData.Data, std::move(entry)).first; } if(!iterCache->second.Routes.empty()) { uint32_t seed = uint32_t(nodeData.Data) * 2654435761u; seed ^= uint32_t(x) * 73856093u; seed ^= uint32_t(y) * 19349663u; seed ^= uint32_t(z) * 83492791u; for(size_t routeIndex = 0; routeIndex < iterCache->second.Routes.size(); routeIndex++) { const auto& variants = iterCache->second.Routes[routeIndex]; const auto* faces = pickVariant(variants, seed + uint32_t(routeIndex) * 374761393u); if(faces) appendModel(*faces, fullCovered, x, y, z); } usedModel = true; } } if(usedModel) goto node_done; if(NSP && node->TexId != 0) { auto iterTex = baseTextureCache.find(node->TexId); if(iterTex != baseTextureCache.end()) { v.Tex = iterTex->second; } else { uint32_t resolvedTex = NSP->getTextureId(node->TexId); v.Tex = resolvedTex; baseTextureCache.emplace(node->TexId, resolvedTex); } } else { v.Tex = node->TexId; } if(v.Tex == 0) goto node_done; // Рендерим обычный кубоид // XZ+Y if(!(fullCovered & 0b000100)) { v.FX = 224+x*64; v.FY = 224+y*64+64; v.FZ = 224+z*64+64; v.TU = 0; v.TV = 0; result.NodeVertexs.push_back(v); v.FX += 64; v.TU = 65535; result.NodeVertexs.push_back(v); v.FZ -= 64; v.TV = 65535; result.NodeVertexs.push_back(v); v.FX = 224+x*64; v.FZ = 224+z*64+64; v.TU = 0; v.TV = 0; result.NodeVertexs.push_back(v); v.FX += 64; v.FZ -= 64; v.TV = 65535; v.TU = 65535; result.NodeVertexs.push_back(v); v.FX -= 64; v.TU = 0; result.NodeVertexs.push_back(v); } // XZ-Y if(!(fullCovered & 0b001000)) { v.FX = 224+x*64; v.FY = 224+y*64; v.FZ = 224+z*64+64; v.TU = 0; v.TV = 0; result.NodeVertexs.push_back(v); v.FZ -= 64; v.TV = 65535; result.NodeVertexs.push_back(v); v.FX += 64; v.TU = 65535; result.NodeVertexs.push_back(v); v.FX = 224+x*64; v.FZ = 224+z*64+64; v.TU = 0; v.TV = 0; result.NodeVertexs.push_back(v); v.FX += 64; v.FZ -= 64; v.TV = 65535; v.TU = 65535; result.NodeVertexs.push_back(v); v.FZ += 64; v.TV = 0; result.NodeVertexs.push_back(v); } //YZ+X if(!(fullCovered & 0b000001)) { v.FX = 224+x*64+64; v.FY = 224+y*64; v.FZ = 224+z*64+64; v.TU = 0; v.TV = 0; result.NodeVertexs.push_back(v); v.FZ -= 64; v.TV = 65535; result.NodeVertexs.push_back(v); v.FY += 64; v.TU = 65535; result.NodeVertexs.push_back(v); v.FY = 224+y*64; v.FZ = 224+z*64+64; v.TU = 0; v.TV = 0; result.NodeVertexs.push_back(v); v.FY += 64; v.FZ -= 64; v.TV = 65535; v.TU = 65535; result.NodeVertexs.push_back(v); v.FZ += 64; v.TV = 0; result.NodeVertexs.push_back(v); } //YZ-X if(!(fullCovered & 0b000010)) { v.FX = 224+x*64; v.FY = 224+y*64; v.FZ = 224+z*64+64; v.TU = 0; v.TV = 0; result.NodeVertexs.push_back(v); v.FY += 64; v.TU = 65535; result.NodeVertexs.push_back(v); v.FZ -= 64; v.TV = 65535; result.NodeVertexs.push_back(v); v.FY = 224+y*64; v.FZ = 224+z*64+64; v.TU = 0; v.TV = 0; result.NodeVertexs.push_back(v); v.FY += 64; v.FZ -= 64; v.TV = 65535; v.TU = 65535; result.NodeVertexs.push_back(v); v.FY -= 64; v.TU = 0; result.NodeVertexs.push_back(v); } //XY+Z if(!(fullCovered & 0b010000)) { v.FX = 224+x*64; v.FY = 224+y*64; v.FZ = 224+z*64+64; v.TU = 0; v.TV = 0; result.NodeVertexs.push_back(v); v.FX += 64; v.TU = 65535; result.NodeVertexs.push_back(v); v.FY += 64; v.TV = 65535; result.NodeVertexs.push_back(v); v.FX = 224+x*64; v.FY = 224+y*64; v.TU = 0; v.TV = 0; result.NodeVertexs.push_back(v); v.FX += 64; v.FY += 64; v.TV = 65535; v.TU = 65535; result.NodeVertexs.push_back(v); v.FX -= 64; v.TU = 0; result.NodeVertexs.push_back(v); } // XY-Z if(!(fullCovered & 0b100000)) { v.FX = 224+x*64; v.FY = 224+y*64; v.FZ = 224+z*64; v.TU = 0; v.TV = 0; result.NodeVertexs.push_back(v); v.FY += 64; v.TV = 65535; result.NodeVertexs.push_back(v); v.FX += 64; v.TU = 65535; result.NodeVertexs.push_back(v); v.FX = 224+x*64; v.FY = 224+y*64; v.TU = 0; v.TV = 0; result.NodeVertexs.push_back(v); v.FX += 64; v.FY += 64; v.TV = 65535; v.TU = 65535; result.NodeVertexs.push_back(v); v.FY -= 64; v.TV = 0; result.NodeVertexs.push_back(v); } node_done: if(debugMeshEnabled) { const bool emitted = result.NodeVertexs.size() > vertexStart; if(emitted) { generatedColumnX[x] = 1; generatedColumnY[y] = 1; generatedColumnZ[z] = 1; } else { const bool hasRenderable = (node->NodestateId != 0) || (node->TexId != 0); if(hasRenderable && fullCovered != 0b111111) { uint32_t warnIndex = debugMeshWarnCount.fetch_add(1); if(warnIndex < 16) { LOG.warn() << "Missing node geometry at chunk " << int(pos[0]) << ',' << int(pos[1]) << ',' << int(pos[2]) << " local " << x << ',' << y << ',' << z << " nodeId " << nodeData.NodeId << " meta " << int(nodeData.Meta) << " covered " << fullCovered << " tex " << node->TexId << " nodestate " << node->NodestateId; } } } } } if(debugMeshEnabled) { auto collectMissing = [](const std::array& expected, const std::array& generated) { std::string res; for(int i = 0; i < 16; i++) { if(expected[i] && !generated[i]) { if(!res.empty()) res += ','; res += std::to_string(i); } } return res; }; std::string missingX = collectMissing(expectedColumnX, generatedColumnX); std::string missingY = collectMissing(expectedColumnY, generatedColumnY); std::string missingZ = collectMissing(expectedColumnZ, generatedColumnZ); if(!missingX.empty() || !missingY.empty() || !missingZ.empty()) { uint32_t warnIndex = debugMeshWarnCount.fetch_add(1); if(warnIndex < 16) { LOG.warn() << "Missing mesh columns at chunk " << int(pos[0]) << ',' << int(pos[1]) << ',' << int(pos[2]) << " missingX[" << missingX << "]" << " missingY[" << missingY << "]" << " missingZ[" << missingZ << "]"; } } } // Вычислить индексы и сократить вершины { uint32_t nextIndex = 0; std::vector vertexes; std::unordered_map vertexTable; std::vector indexes; for(const NodeVertexStatic& vertex : result.NodeVertexs) { auto iter = vertexTable.find(vertex); if(iter == vertexTable.end()) { vertexTable.insert({vertex, nextIndex}); vertexes.push_back(vertex); indexes.push_back(nextIndex); nextIndex += 1; } else { indexes.push_back(iter->second); } } result.NodeVertexs = std::move(vertexes); if(nextIndex <= (1 << 16)) { std::vector indexes16; indexes16.reserve(indexes.size()); for(size_t iter = 0; iter < indexes.size(); iter++) indexes16.push_back(indexes[iter]); result.NodeIndexes = std::move(indexes16); } else { result.NodeIndexes = std::move(indexes); } } } end: Output.lock()->emplace_back(std::move(result)); } } catch(const std::exception& exc) { LOG.debug() << "Ошибка в работе потока:\n" << exc.what(); Sync.NeedShutdown = true; } { std::unique_lock lock(Sync.Mutex); Sync.CountInRun -= 1; Sync.CV_CountInRun.notify_all(); } LOG.debug() << "Завершение потока верширования чанков"; } void ChunkPreparator::tickSync(const TickSyncData& data) { // Обработать изменения в чанках // Пересчёт соседних чанков // Проверить необходимость пересчёта чанков при изменении профилей std::unordered_map> changedChunks = data.ChangedChunks; if(!data.ChangedNodes.empty()) { std::unordered_set changedNodes(data.ChangedNodes.begin(), data.ChangedNodes.end()); for(const auto& [wId, regions] : ChunksMesh) { for(const auto& [rPos, chunks] : regions) { Pos::GlobalChunk base = Pos::GlobalChunk(rPos) << 2; for(size_t index = 0; index < chunks.size(); index++) { const ChunkObj_t& chunk = chunks[index]; if(chunk.Nodes.empty()) continue; bool hit = false; for(DefNodeId nodeId : chunk.Nodes) { if(changedNodes.contains(nodeId)) { hit = true; break; } } if(!hit) continue; Pos::bvec4u localPos; localPos.unpack(index); changedChunks[wId].push_back(base + Pos::GlobalChunk(localPos)); } } } } if(!data.ChangedVoxels.empty()) { std::unordered_set changedVoxels(data.ChangedVoxels.begin(), data.ChangedVoxels.end()); for(const auto& [wId, regions] : ChunksMesh) { for(const auto& [rPos, chunks] : regions) { Pos::GlobalChunk base = Pos::GlobalChunk(rPos) << 2; for(size_t index = 0; index < chunks.size(); index++) { const ChunkObj_t& chunk = chunks[index]; if(chunk.Voxels.empty()) continue; bool hit = false; for(DefVoxelId voxelId : chunk.Voxels) { if(changedVoxels.contains(voxelId)) { hit = true; break; } } if(!hit) continue; Pos::bvec4u localPos; localPos.unpack(index); changedChunks[wId].push_back(base + Pos::GlobalChunk(localPos)); } } } } // Добавляем к изменёным чанкам пересчёт соседей { std::vector> toBuild; for(auto& [wId, chunks] : changedChunks) { std::vector list; for(const Pos::GlobalChunk& pos : chunks) { list.push_back(pos); list.push_back(pos+Pos::GlobalChunk(1, 0, 0)); list.push_back(pos+Pos::GlobalChunk(-1, 0, 0)); list.push_back(pos+Pos::GlobalChunk(0, 1, 0)); list.push_back(pos+Pos::GlobalChunk(0, -1, 0)); list.push_back(pos+Pos::GlobalChunk(0, 0, 1)); list.push_back(pos+Pos::GlobalChunk(0, 0, -1)); } std::sort(list.begin(), list.end()); auto eraseIter = std::unique(list.begin(), list.end()); list.erase(eraseIter, list.end()); for(Pos::GlobalChunk& pos : list) { Pos::GlobalRegion rPos = pos >> 2; auto iterRegion = Requests[wId].find(rPos); if(iterRegion != Requests[wId].end()) toBuild.emplace_back(wId, pos, iterRegion->second); else toBuild.emplace_back(wId, pos, Requests[wId][rPos] = NextRequest++); } } CMG.Input.lock()->push_range(toBuild); } // Чистим запросы и чанки { uint8_t frameRetirement = (FrameRoulette+FRAME_COUNT_RESOURCE_LATENCY) % FRAME_COUNT_RESOURCE_LATENCY; for(auto& [wId, regions] : data.LostRegions) { if(auto iterWorld = Requests.find(wId); iterWorld != Requests.end()) { for(const Pos::GlobalRegion& rPos : regions) if(auto iterRegion = iterWorld->second.find(rPos); iterRegion != iterWorld->second.end()) iterWorld->second.erase(iterRegion); } if(auto iterWorld = ChunksMesh.find(wId); iterWorld != ChunksMesh.end()) { for(const Pos::GlobalRegion& rPos : regions) if(auto iterRegion = iterWorld->second.find(rPos); iterRegion != iterWorld->second.end()) { for(int iter = 0; iter < 4*4*4; iter++) { auto& chunk = iterRegion->second[iter]; if(chunk.VoxelPointer) VPV_ToFree[frameRetirement].emplace_back(std::move(chunk.VoxelPointer)); if(chunk.NodePointer) { VPN_ToFree[frameRetirement].emplace_back(std::move(chunk.NodePointer), std::move(chunk.NodeIndexes)); } } iterWorld->second.erase(iterRegion); } } } } // Получаем готовые чанки { std::vector chunks = std::move(*CMG.Output.lock()); for(auto& chunk : chunks) { auto iterWorld = Requests.find(chunk.WId); if(iterWorld == Requests.end()) continue; auto iterRegion = iterWorld->second.find(chunk.Pos >> 2); if(iterRegion == iterWorld->second.end()) continue; if(iterRegion->second != chunk.RequestId) continue; // Чанк ожидаем auto& rChunk = ChunksMesh[chunk.WId][chunk.Pos >> 2][Pos::bvec4u(chunk.Pos & 0x3).pack()]; rChunk.Voxels = std::move(chunk.VoxelDefines); if(!chunk.VoxelVertexs.empty()) rChunk.VoxelPointer = VertexPool_Voxels.pushVertexs(std::move(chunk.VoxelVertexs)); rChunk.Nodes = std::move(chunk.NodeDefines); if(!chunk.NodeVertexs.empty()) rChunk.NodePointer = VertexPool_Nodes.pushVertexs(std::move(chunk.NodeVertexs)); if(std::vector* ptr = std::get_if>(&chunk.NodeIndexes)) { if(!ptr->empty()) rChunk.NodeIndexes = IndexPool_Nodes_16.pushVertexs(std::move(*ptr)); } else if(std::vector* ptr = std::get_if>(&chunk.NodeIndexes)) { if(!ptr->empty()) rChunk.NodeIndexes = IndexPool_Nodes_32.pushVertexs(std::move(*ptr)); } } } CMG.endTickSync(); } void ChunkPreparator::pushFrame() { FrameRoulette = (FrameRoulette+1) % FRAME_COUNT_RESOURCE_LATENCY; for(auto pointer : VPV_ToFree[FrameRoulette]) { VertexPool_Voxels.dropVertexs(pointer); } VPV_ToFree[FrameRoulette].clear(); for(auto& pointer : VPN_ToFree[FrameRoulette]) { VertexPool_Nodes.dropVertexs(std::get<0>(pointer)); if(IndexPool::Pointer* ind = std::get_if::Pointer>(&std::get<1>(pointer))) { IndexPool_Nodes_16.dropVertexs(*ind); } else if(IndexPool::Pointer* ind = std::get_if::Pointer>(&std::get<1>(pointer))) { IndexPool_Nodes_32.dropVertexs(*ind); } } VPN_ToFree[FrameRoulette].clear(); } std::pair< std::vector, uint32_t>>, std::vector, std::pair, bool, uint32_t>> > ChunkPreparator::getChunksForRender( WorldId_t worldId, Pos::Object pos, uint8_t distance, glm::mat4 projView, Pos::GlobalRegion x64offset ) { Pos::GlobalChunk playerChunk = pos >> Pos::Object_t::BS_Bit >> 4; Pos::GlobalRegion center = playerChunk >> 2; std::vector, uint32_t>> vertexVoxels; std::vector, std::pair, bool, uint32_t>> vertexNodes; auto iterWorld = ChunksMesh.find(worldId); if(iterWorld == ChunksMesh.end()) return {}; Frustum fr(projView); for(int z = -distance; z <= distance; z++) { for(int y = -distance; y <= distance; y++) { for(int x = -distance; x <= distance; x++) { Pos::GlobalRegion region = center + Pos::GlobalRegion(x, y, z); glm::vec3 begin = glm::vec3(region - x64offset) * 64.f; glm::vec3 end = begin + glm::vec3(64.f); if(!fr.IsBoxVisible(begin, end)) continue; auto iterRegion = iterWorld->second.find(region); if(iterRegion == iterWorld->second.end()) continue; Pos::GlobalChunk local = Pos::GlobalChunk(region) << 2; for(size_t index = 0; index < iterRegion->second.size(); index++) { Pos::bvec4u localPos; localPos.unpack(index); glm::vec3 chunkPos = begin+glm::vec3(localPos)*16.f; if(!fr.IsBoxVisible(chunkPos, chunkPos+glm::vec3(16))) continue; auto &chunk = iterRegion->second[index]; float distance; if(chunk.VoxelPointer || chunk.NodePointer) { Pos::GlobalChunk cp = local+Pos::GlobalChunk(localPos)-playerChunk; distance = cp.x*cp.x+cp.y*cp.y+cp.z*cp.z; } if(chunk.VoxelPointer) { vertexVoxels.emplace_back(distance, local+Pos::GlobalChunk(localPos), VertexPool_Voxels.map(chunk.VoxelPointer), chunk.VoxelPointer.VertexCount); } if(chunk.NodePointer) { vertexNodes.emplace_back( distance, local+Pos::GlobalChunk(localPos), VertexPool_Nodes.map(chunk.NodePointer), chunk.NodeIndexes.index() == 0 ? IndexPool_Nodes_16.map(std::get<0>(chunk.NodeIndexes)) : IndexPool_Nodes_32.map(std::get<1>(chunk.NodeIndexes)) , chunk.NodeIndexes.index() == 0, std::visit([](const auto& val) -> uint32_t { return val.VertexCount; }, chunk.NodeIndexes)); } } } } } { auto sortByDistance = [] ( const std::tuple, uint32_t>& a, const std::tuple, uint32_t>& b ) { return std::get<0>(a) < std::get<0>(b); }; std::sort(vertexVoxels.begin(), vertexVoxels.end(), sortByDistance); } { auto sortByDistance = [] ( const std::tuple, std::pair, bool, uint32_t>& a, const std::tuple, std::pair, bool, uint32_t>& b ) { return std::get<0>(a) < std::get<0>(b); }; std::sort(vertexNodes.begin(), vertexNodes.end(), sortByDistance); } std::vector, uint32_t>> resVertexVoxels; std::vector, std::pair, bool, uint32_t>> resVertexNodes; resVertexVoxels.reserve(vertexVoxels.size()); resVertexNodes.reserve(vertexNodes.size()); for(auto& [d, pos, ptr, count] : vertexVoxels) resVertexVoxels.emplace_back(pos, std::move(ptr), count); for(auto& [d, pos, ptr, ptr2, type, count] : vertexNodes) resVertexNodes.emplace_back(pos, std::move(ptr), std::move(ptr2), type, count); return std::pair{std::move(resVertexVoxels), std::move(resVertexNodes)}; } VulkanRenderSession::VulkanRenderSession(Vulkan *vkInst, IServerSession *serverSession) : VkInst(vkInst), ServerSession(serverSession), CP(vkInst, serverSession), LightDummy(vkInst), TestQuad(vkInst, sizeof(NodeVertexStatic)*6*3*2) { assert(vkInst); assert(serverSession); { std::vector poolSizes { {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 3}, {VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 3}, {VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 3} }; VkDescriptorPoolCreateInfo descriptorPool = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, .flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, .maxSets = 8, .poolSizeCount = (uint32_t) poolSizes.size(), .pPoolSizes = poolSizes.data() }; vkAssert(!vkCreateDescriptorPool(VkInst->Graphics.Device, &descriptorPool, nullptr, &DescriptorPool)); } TP = std::make_unique(VkInst, DescriptorPool); NSP = std::make_unique(MP, *TP); CP.setNodestateProvider(NSP.get()); { std::vector shaderLayoutBindings = { { .binding = 0, .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, .descriptorCount = 1, .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, .pImmutableSamplers = nullptr }, { .binding = 1, .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, .descriptorCount = 1, .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, .pImmutableSamplers = nullptr } }; const VkDescriptorSetLayoutCreateInfo descriptorLayout = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, .pNext = nullptr, .flags = 0, .bindingCount = (uint32_t) shaderLayoutBindings.size(), .pBindings = shaderLayoutBindings.data() }; vkAssert(!vkCreateDescriptorSetLayout(VkInst->Graphics.Device, &descriptorLayout, nullptr, &VoxelLightMapDescLayout)); } { VkDescriptorSetAllocateInfo ciAllocInfo = { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, .pNext = nullptr, .descriptorPool = DescriptorPool, .descriptorSetCount = 1, .pSetLayouts = &VoxelLightMapDescLayout }; vkAssert(!vkAllocateDescriptorSets(VkInst->Graphics.Device, &ciAllocInfo, &VoxelLightMapDescriptor)); } LightDummy.atlasAddCallbackOnUniformChange([this]() -> bool { updateDescriptor_VoxelsLight(); return true; }); /* x left -1 ~ right 1 y up 1 ~ down -1 z near 0 ~ far -1 glm */ { NodeVertexStatic *array = (NodeVertexStatic*) TestQuad.mapMemory(); array[0] = {224, 224, 0, 224, 0, 0, 0, 65535, 0}; array[1] = {224, 224+64, 0, 224, 0, 0, 0, 0, 65535}; array[2] = {224+64, 224+64, 0, 224, 0, 0, 0, 0, 65535}; array[3] = {224, 224, 0, 224, 0, 0, 0, 65535, 0}; array[4] = {224+64, 224+64, 0, 224, 0, 0, 0, 0, 65535}; array[5] = {224+64, 224, 0, 224, 0, 0, 0, 0, 0}; array[6] = {224, 224, 0, 224+64, 0, 0, 0, 0, 0}; array[7] = {224+64, 224, 0, 224+64, 0, 0, 0, 65535, 0}; array[8] = {224+64, 224+64, 0, 224+64, 0, 0, 0, 65535, 65535}; array[9] = {224, 224, 0, 224+64, 0, 0, 0, 0, 0}; array[10] = {224+64, 224+64, 0, 224+64, 0, 0, 0, 65535, 65535}; array[11] = {224, 224+64, 0, 224+64, 0, 0, 0, 0, 65535}; array[12] = {224, 224, 0, 224, 0, 0, 0, 0, 0}; array[13] = {224, 224, 0, 224+64, 0, 0, 0, 65535, 0}; array[14] = {224, 224+64, 0, 224+64, 0, 0, 0, 65535, 65535}; array[15] = {224, 224, 0, 224, 0, 0, 0, 0, 0}; array[16] = {224, 224+64, 0, 224+64, 0, 0, 0, 65535, 65535}; array[17] = {224, 224+64, 0, 224, 0, 0, 0, 0, 65535}; array[18] = {224+64, 224, 0, 224+64, 0, 0, 0, 0, 0}; array[19] = {224+64, 224, 0, 224, 0, 0, 0, 65535, 0}; array[20] = {224+64, 224+64, 0, 224, 0, 0, 0, 65535, 65535}; array[21] = {224+64, 224, 0, 224+64, 0, 0, 0, 0, 0}; array[22] = {224+64, 224+64, 0, 224, 0, 0, 0, 65535, 65535}; array[23] = {224+64, 224+64, 0, 224+64, 0, 0, 0, 0, 65535}; array[24] = {224, 224, 0, 224, 0, 0, 0, 0, 0}; array[25] = {224+64, 224, 0, 224, 0, 0, 0, 65535, 0}; array[26] = {224+64, 224, 0, 224+64, 0, 0, 0, 65535, 65535}; array[27] = {224, 224, 0, 224, 0, 0, 0, 0, 0}; array[28] = {224+64, 224, 0, 224+64, 0, 0, 0, 65535, 65535}; array[29] = {224, 224, 0, 224+64, 0, 0, 0, 0, 65535}; array[30] = {224, 224+64, 0, 224+64, 0, 0, 0, 0, 0}; array[31] = {224+64, 224+64, 0, 224+64, 0, 0, 0, 65535, 0}; array[32] = {224+64, 224+64, 0, 224, 0, 0, 0, 65535, 65535}; array[33] = {224, 224+64, 0, 224+64, 0, 0, 0, 0, 0}; array[34] = {224+64, 224+64, 0, 224, 0, 0, 0, 65535, 65535}; array[35] = {224, 224+64, 0, 224, 0, 0, 0, 0, 65535}; for(int iter = 0; iter < 36; iter++) { array[iter].Tex = 6; if(array[iter].FX == 135) array[iter].FX--; else array[iter].FX++; if(array[iter].FY == 135) array[iter].FY--; else array[iter].FY++; if(array[iter].FZ == 135) array[iter].FZ--; else array[iter].FZ++; } TestQuad.unMapMemory(); } { std::vector cubes; cubes.push_back({0, 0, Pos::bvec256u{0, 0, 0}, Pos::bvec256u{0, 0, 0}}); cubes.push_back({1, 0, Pos::bvec256u{255, 0, 0}, Pos::bvec256u{0, 0, 0}}); cubes.push_back({1, 0, Pos::bvec256u{0, 255, 0}, Pos::bvec256u{0, 0, 0}}); cubes.push_back({1, 0, Pos::bvec256u{0, 0, 255}, Pos::bvec256u{0, 0, 0}}); cubes.push_back({2, 0, Pos::bvec256u{255, 255, 0}, Pos::bvec256u{0, 0, 0}}); cubes.push_back({2, 0, Pos::bvec256u{0, 255, 255}, Pos::bvec256u{0, 0, 0}}); cubes.push_back({2, 0, Pos::bvec256u{255, 0, 255}, Pos::bvec256u{0, 0, 0}}); cubes.push_back({3, 0, Pos::bvec256u{255, 255, 255}, Pos::bvec256u{0, 0, 0}}); cubes.push_back({4, 0, Pos::bvec256u{64, 64, 64}, Pos::bvec256u{127, 127, 127}}); std::vector vertexs = generateMeshForVoxelChunks(cubes); if(!vertexs.empty()) { TestVoxel.emplace(VkInst, vertexs.size()*sizeof(VoxelVertexPoint)); std::copy(vertexs.data(), vertexs.data()+vertexs.size(), (VoxelVertexPoint*) TestVoxel->mapMemory()); TestVoxel->unMapMemory(); } } updateDescriptor_VoxelsLight(); updateDescriptor_ChunksLight(); // Разметка графических конвейеров { std::vector worldWideShaderPushConstants = { { .stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_GEOMETRY_BIT, .offset = 0, .size = uint32_t(sizeof(WorldPCO)) } }; std::vector layouts = { TP ? TP->getDescriptorLayout() : VK_NULL_HANDLE, VoxelLightMapDescLayout }; const VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = { .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, .pNext = nullptr, .flags = 0, .setLayoutCount = (uint32_t) layouts.size(), .pSetLayouts = layouts.data(), .pushConstantRangeCount = (uint32_t) worldWideShaderPushConstants.size(), .pPushConstantRanges = worldWideShaderPushConstants.data() }; vkAssert(!vkCreatePipelineLayout(VkInst->Graphics.Device, &pPipelineLayoutCreateInfo, nullptr, &MainAtlas_LightMap_PipelineLayout)); } // Настройка мультисемплинга // Может нужно будет в будущем связать с настройками главного буфера VkPipelineMultisampleStateCreateInfo multisample = { .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO, .pNext = nullptr, .flags = 0, .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT, .sampleShadingEnable = false, .minSampleShading = 0.0f, .pSampleMask = nullptr, .alphaToCoverageEnable = false, .alphaToOneEnable = false }; // Конвейеры для вокселей и нод { VkPipelineCacheCreateInfo infoPipelineCache { .sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO, .pNext = nullptr, .flags = 0, .initialDataSize = 0, .pInitialData = nullptr }; // Для статичных непрозрачных и полупрозрачных вокселей if(!VoxelShaderVertex) VoxelShaderVertex = VkInst->createShader(getResource("shaders/chunk/voxel.vert.bin")->makeView()); if(!VoxelShaderGeometry) VoxelShaderGeometry = VkInst->createShader(getResource("shaders/chunk/voxel.geom.bin")->makeView()); if(!VoxelShaderFragmentOpaque) VoxelShaderFragmentOpaque = VkInst->createShader(getResource("shaders/chunk/voxel_opaque.frag.bin")->makeView()); if(!VoxelShaderFragmentTransparent) VoxelShaderFragmentTransparent = VkInst->createShader(getResource("shaders/chunk/voxel_transparent.frag.bin")->makeView()); // Конвейер шейдеров std::vector shaderStages = { { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .pNext = nullptr, .flags = 0, .stage = VK_SHADER_STAGE_VERTEX_BIT, .module = *VoxelShaderVertex, .pName = "main", .pSpecializationInfo = nullptr }, { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .pNext = nullptr, .flags = 0, .stage = VK_SHADER_STAGE_GEOMETRY_BIT, .module = *VoxelShaderGeometry, .pName = "main", .pSpecializationInfo = nullptr }, { .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, .pNext = nullptr, .flags = 0, .stage = VK_SHADER_STAGE_FRAGMENT_BIT, .module = *VoxelShaderFragmentOpaque, .pName = "main", .pSpecializationInfo = nullptr } }; // Вершины шейдера // Настройка формата вершин шейдера std::vector shaderVertexBindings = { { .binding = 0, .stride = sizeof(VoxelVertexPoint), .inputRate = VK_VERTEX_INPUT_RATE_VERTEX } }; std::vector shaderVertexAttribute = { { .location = 0, .binding = 0, .format = VK_FORMAT_R32G32B32_UINT, .offset = 0 } }; VkPipelineVertexInputStateCreateInfo createInfoVertexInput = { .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO, .pNext = nullptr, .flags = 0, .vertexBindingDescriptionCount = (uint32_t) shaderVertexBindings.size(), .pVertexBindingDescriptions = shaderVertexBindings.data(), .vertexAttributeDescriptionCount = (uint32_t) shaderVertexAttribute.size(), .pVertexAttributeDescriptions = shaderVertexAttribute.data() }; // Топология вершин на входе (треугольники, линии, точки) VkPipelineInputAssemblyStateCreateInfo ia = { .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO, .pNext = nullptr, .flags = 0, .topology = VK_PRIMITIVE_TOPOLOGY_POINT_LIST, .primitiveRestartEnable = false }; VkPipelineViewportStateCreateInfo vp = { .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO, .pNext = nullptr, .flags = 0, .viewportCount = 1, .pViewports = nullptr, .scissorCount = 1, .pScissors = nullptr }; // Настройки растеризатора VkPipelineRasterizationStateCreateInfo rasterization = { .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO, .pNext = nullptr, .flags = 0, .depthClampEnable = false, .rasterizerDiscardEnable = false, .polygonMode = VK_POLYGON_MODE_FILL, .cullMode = VK_CULL_MODE_BACK_BIT, .frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE, .depthBiasEnable = false, .depthBiasConstantFactor = 0.0f, .depthBiasClamp = 0.0f, .depthBiasSlopeFactor = 0.0f, .lineWidth = 1.0f }; // Тест буфера глубины и трафарета VkPipelineDepthStencilStateCreateInfo depthStencil = { .sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO, .pNext = nullptr, .flags = 0, .depthTestEnable = true, .depthWriteEnable = true, .depthCompareOp = VK_COMPARE_OP_LESS, .depthBoundsTestEnable = false, .stencilTestEnable = false, .front = VkStencilOpState { .failOp = VK_STENCIL_OP_KEEP, .passOp = VK_STENCIL_OP_KEEP, .depthFailOp = VK_STENCIL_OP_KEEP, .compareOp = VK_COMPARE_OP_ALWAYS, .compareMask = 0x0, .writeMask = 0x0, .reference = 0x0 }, .back = VkStencilOpState { .failOp = VK_STENCIL_OP_KEEP, .passOp = VK_STENCIL_OP_KEEP, .depthFailOp = VK_STENCIL_OP_KEEP, .compareOp = VK_COMPARE_OP_ALWAYS, .compareMask = 0x0, .writeMask = 0x0, .reference = 0x0 }, .minDepthBounds = 0.0f, .maxDepthBounds = 0.0f }; // Логика смешивания цветов std::vector colorBlend = { { .blendEnable = false, .srcColorBlendFactor = VK_BLEND_FACTOR_ZERO, .dstColorBlendFactor = VK_BLEND_FACTOR_ONE, .colorBlendOp = VK_BLEND_OP_ADD, .srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, .alphaBlendOp = VK_BLEND_OP_ADD, .colorWriteMask = 0xf } }; VkPipelineColorBlendStateCreateInfo cb = { .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, .pNext = nullptr, .flags = 0, .logicOpEnable = VK_FALSE, .logicOp = VK_LOGIC_OP_CLEAR, .attachmentCount = (uint32_t) colorBlend.size(), .pAttachments = colorBlend.data(), .blendConstants = {0.f, 0.f, 0.f, 0.f} }; // Настройки конвейера, которые могут быть изменены без пересоздания конвейера std::vector dynamicStates = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; VkPipelineDynamicStateCreateInfo dynamicState = { .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, .pNext = nullptr, .flags = 0, .dynamicStateCount = (uint32_t) dynamicStates.size(), .pDynamicStates = dynamicStates.data(), }; VkGraphicsPipelineCreateInfo pipeline = { .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO, .pNext = nullptr, .flags = 0, .stageCount = (uint32_t) shaderStages.size(), .pStages = shaderStages.data(), .pVertexInputState = &createInfoVertexInput, .pInputAssemblyState = &ia, .pTessellationState = nullptr, .pViewportState = &vp, .pRasterizationState = &rasterization, .pMultisampleState = &multisample, .pDepthStencilState = &depthStencil, .pColorBlendState = &cb, .pDynamicState = &dynamicState, .layout = MainAtlas_LightMap_PipelineLayout, .renderPass = VkInst->Graphics.RenderPass, .subpass = 0, .basePipelineHandle = nullptr, .basePipelineIndex = 0 }; if(!VoxelOpaquePipeline) vkAssert(!vkCreateGraphicsPipelines(VkInst->Graphics.Device, VK_NULL_HANDLE, 1, &pipeline, nullptr, &VoxelOpaquePipeline)); if(!VoxelTransparentPipeline) { shaderStages[2].module = *VoxelShaderFragmentTransparent, colorBlend[0] = { .blendEnable = VK_TRUE, .srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA, .dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, .colorBlendOp = VK_BLEND_OP_ADD, .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE, .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, .alphaBlendOp = VK_BLEND_OP_ADD, .colorWriteMask = 0xf }; vkAssert(!vkCreateGraphicsPipelines(VkInst->Graphics.Device, VK_NULL_HANDLE, 1, &pipeline, nullptr, &VoxelTransparentPipeline)); } // Для статичных непрозрачных и полупрозрачных нод if(!NodeShaderVertex) NodeShaderVertex = VkInst->createShader(getResource("shaders/chunk/node.vert.bin")->makeView()); if(!NodeShaderGeometry) NodeShaderGeometry = VkInst->createShader(getResource("shaders/chunk/node.geom.bin")->makeView()); if(!NodeShaderFragmentOpaque) NodeShaderFragmentOpaque = VkInst->createShader(getResource("shaders/chunk/node_opaque.frag.bin")->makeView()); if(!NodeShaderFragmentTransparent) NodeShaderFragmentTransparent = VkInst->createShader(getResource("shaders/chunk/node_transparent.frag.bin")->makeView()); ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, shaderStages[0].module = *NodeShaderVertex; shaderStages[1].module = *NodeShaderGeometry; shaderStages[2].module = *NodeShaderFragmentOpaque; colorBlend[0] = { .blendEnable = false, .srcColorBlendFactor = VK_BLEND_FACTOR_ZERO, .dstColorBlendFactor = VK_BLEND_FACTOR_ONE, .colorBlendOp = VK_BLEND_OP_ADD, .srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, .alphaBlendOp = VK_BLEND_OP_ADD, .colorWriteMask = 0xf }; if(!NodeStaticOpaquePipeline) { vkAssert(!vkCreateGraphicsPipelines(VkInst->Graphics.Device, VK_NULL_HANDLE, 1, &pipeline, nullptr, &NodeStaticOpaquePipeline)); } if(!NodeStaticTransparentPipeline) { shaderStages[2].module = *NodeShaderFragmentTransparent; colorBlend[0] = { .blendEnable = VK_TRUE, .srcColorBlendFactor = VK_BLEND_FACTOR_SRC_ALPHA, .dstColorBlendFactor = VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, .colorBlendOp = VK_BLEND_OP_ADD, .srcAlphaBlendFactor = VK_BLEND_FACTOR_ONE, .dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO, .alphaBlendOp = VK_BLEND_OP_ADD, .colorWriteMask = 0xf }; vkAssert(!vkCreateGraphicsPipelines(VkInst->Graphics.Device, VK_NULL_HANDLE, 1, &pipeline, nullptr, &NodeStaticTransparentPipeline)); } } } VulkanRenderSession::~VulkanRenderSession() { if(VoxelOpaquePipeline) vkDestroyPipeline(VkInst->Graphics.Device, VoxelOpaquePipeline, nullptr); if(VoxelTransparentPipeline) vkDestroyPipeline(VkInst->Graphics.Device, VoxelTransparentPipeline, nullptr); if(NodeStaticOpaquePipeline) vkDestroyPipeline(VkInst->Graphics.Device, NodeStaticOpaquePipeline, nullptr); if(NodeStaticTransparentPipeline) vkDestroyPipeline(VkInst->Graphics.Device, NodeStaticTransparentPipeline, nullptr); if(MainAtlas_LightMap_PipelineLayout) vkDestroyPipelineLayout(VkInst->Graphics.Device, MainAtlas_LightMap_PipelineLayout, nullptr); if(VoxelLightMapDescLayout) vkDestroyDescriptorSetLayout(VkInst->Graphics.Device, VoxelLightMapDescLayout, nullptr); if(DescriptorPool) vkDestroyDescriptorPool(VkInst->Graphics.Device, DescriptorPool, nullptr); } void VulkanRenderSession::prepareTickSync() { CP.prepareTickSync(); } void VulkanRenderSession::pushStageTickSync() { CP.pushStageTickSync(); } void VulkanRenderSession::tickSync(const TickSyncData& data) { // Изменение ассетов // Профили // Чанки ChunkPreparator::TickSyncData mcpData; mcpData.ChangedChunks = data.Chunks_ChangeOrAdd; mcpData.LostRegions = data.Chunks_Lost; if(auto iter = data.Profiles_ChangeOrAdd.find(EnumDefContent::Node); iter != data.Profiles_ChangeOrAdd.end()) mcpData.ChangedNodes.insert(mcpData.ChangedNodes.end(), iter->second.begin(), iter->second.end()); if(auto iter = data.Profiles_Lost.find(EnumDefContent::Node); iter != data.Profiles_Lost.end()) mcpData.ChangedNodes.insert(mcpData.ChangedNodes.end(), iter->second.begin(), iter->second.end()); if(auto iter = data.Profiles_ChangeOrAdd.find(EnumDefContent::Voxel); iter != data.Profiles_ChangeOrAdd.end()) mcpData.ChangedVoxels.insert(mcpData.ChangedVoxels.end(), iter->second.begin(), iter->second.end()); if(auto iter = data.Profiles_Lost.find(EnumDefContent::Voxel); iter != data.Profiles_Lost.end()) mcpData.ChangedVoxels.insert(mcpData.ChangedVoxels.end(), iter->second.begin(), iter->second.end()); std::vector*>> modelResources; std::vector modelLost; if(auto iter = data.Assets_ChangeOrAdd.find(EnumAssets::Model); iter != data.Assets_ChangeOrAdd.end()) { const auto& list = ServerSession->Assets[EnumAssets::Model]; for(ResourceId id : iter->second) { auto entryIter = list.find(id); if(entryIter == list.end()) continue; modelResources.emplace_back(id, entryIter->second.Res, &entryIter->second.Dependencies); } } if(auto iter = data.Assets_Lost.find(EnumAssets::Model); iter != data.Assets_Lost.end()) modelLost.insert(modelLost.end(), iter->second.begin(), iter->second.end()); std::vector changedModels; if(!modelResources.empty() || !modelLost.empty()) { const auto& modelAssets = ServerSession->Assets[EnumAssets::Model]; changedModels = MP.onModelChanges(std::move(modelResources), std::move(modelLost), &modelAssets); } if(TP) { std::vector textureResources; std::vector textureLost; if(auto iter = data.Assets_ChangeOrAdd.find(EnumAssets::Texture); iter != data.Assets_ChangeOrAdd.end()) { const auto& list = ServerSession->Assets[EnumAssets::Texture]; for(ResourceId id : iter->second) { auto entryIter = list.find(id); if(entryIter == list.end()) continue; textureResources.push_back({ .Id = id, .Res = entryIter->second.Res, .Domain = entryIter->second.Domain, .Key = entryIter->second.Key }); } } if(auto iter = data.Assets_Lost.find(EnumAssets::Texture); iter != data.Assets_Lost.end()) textureLost.insert(textureLost.end(), iter->second.begin(), iter->second.end()); if(!textureResources.empty() || !textureLost.empty()) TP->onTexturesChanges(std::move(textureResources), std::move(textureLost)); } std::vector changedNodestates; if(NSP) { std::vector*>> nodestateResources; std::vector nodestateLost; if(auto iter = data.Assets_ChangeOrAdd.find(EnumAssets::Nodestate); iter != data.Assets_ChangeOrAdd.end()) { const auto& list = ServerSession->Assets[EnumAssets::Nodestate]; for(ResourceId id : iter->second) { auto entryIter = list.find(id); if(entryIter == list.end()) continue; nodestateResources.emplace_back(id, entryIter->second.Res, &entryIter->second.Dependencies); } } if(auto iter = data.Assets_Lost.find(EnumAssets::Nodestate); iter != data.Assets_Lost.end()) nodestateLost.insert(nodestateLost.end(), iter->second.begin(), iter->second.end()); if(!nodestateResources.empty() || !nodestateLost.empty() || !changedModels.empty()) changedNodestates = NSP->onNodestateChanges(std::move(nodestateResources), std::move(nodestateLost), changedModels); } if(!changedNodestates.empty()) { std::unordered_set changed; changed.reserve(changedNodestates.size()); for(AssetsNodestate id : changedNodestates) changed.insert(id); for(const auto& [nodeId, def] : ServerSession->Profiles.DefNode) { if(changed.contains(def.NodestateId)) mcpData.ChangedNodes.push_back(nodeId); } } CP.tickSync(mcpData); } void VulkanRenderSession::setCameraPos(WorldId_t worldId, Pos::Object pos, glm::quat quat) { WorldId = worldId; Pos = pos; Quat = quat; WI = worldId; PlayerPos = pos; PlayerPos /= float(Pos::Object_t::BS); } void VulkanRenderSession::beforeDraw(double timeSeconds) { if(TP) TP->update(timeSeconds); LightDummy.atlasUpdateDynamicData(); CP.flushUploadsAndBarriers(VkInst->Graphics.CommandBufferRender); } void VulkanRenderSession::onGpuFinished() { CP.notifyGpuFinished(); } void VulkanRenderSession::drawWorld(GlobalTime gTime, float dTime, VkCommandBuffer drawCmd) { { X64Offset = Pos & ~((1 << Pos::Object_t::BS_Bit << 4 << 2)-1); X64Offset_f = glm::vec3(X64Offset >> Pos::Object_t::BS_Bit); X64Delta = glm::vec3(Pos-X64Offset) / float(Pos::Object_t::BS); } // vkCmdBindPipeline(drawCmd, VK_PIPELINE_BIND_POINT_GRAPHICS, NodeStaticOpaquePipeline); // vkCmdPushConstants(drawCmd, MainAtlas_LightMap_PipelineLayout, // VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_GEOMETRY_BIT, 0, sizeof(WorldPCO), &PCO); // vkCmdBindDescriptorSets(drawCmd, VK_PIPELINE_BIND_POINT_GRAPHICS, // MainAtlas_LightMap_PipelineLayout, 0, 2, // (const VkDescriptorSet[]) {MainAtlasDescriptor, VoxelLightMapDescriptor}, 0, nullptr); // VkDeviceSize vkOffsets = 0; // VkBuffer vkBuffer = VKCTX->TestQuad; // vkCmdBindVertexBuffers(drawCmd, 0, 1, &vkBuffer, &vkOffsets); // for(int i = 0; i < 16; i++) { // PCO.Model = glm::rotate(PCO.Model, glm::half_pi()/4, glm::vec3(0, 1, 0)); // vkCmdPushConstants(drawCmd, MainAtlas_LightMap_PipelineLayout, // VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_GEOMETRY_BIT, 0, sizeof(WorldPCO), &PCO); // vkCmdDraw(drawCmd, 6, 1, 0, 0); // } // PCO.Model = glm::mat4(1); // // Проба рендера вокселей // vkCmdBindPipeline(drawCmd, VK_PIPELINE_BIND_POINT_GRAPHICS, VoxelOpaquePipeline); // vkCmdPushConstants(drawCmd, MainAtlas_LightMap_PipelineLayout, // VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_GEOMETRY_BIT, 0, sizeof(WorldPCO), &PCO); // vkCmdBindDescriptorSets(drawCmd, VK_PIPELINE_BIND_POINT_GRAPHICS, // MainAtlas_LightMap_PipelineLayout, 0, 2, // (const VkDescriptorSet[]) {MainAtlasDescriptor, VoxelLightMapDescriptor}, 0, nullptr); // if(VKCTX->TestVoxel) { // vkBuffer = *VKCTX->TestVoxel; // vkCmdBindVertexBuffers(drawCmd, 0, 1, &vkBuffer, &vkOffsets); // vkCmdDraw(drawCmd, VKCTX->TestVoxel->getSize() / sizeof(VoxelVertexPoint), 1, 0, 0); // } // { // auto iterWorld = External.ChunkVoxelMesh.find(WorldId); // if(iterWorld != External.ChunkVoxelMesh.end()) { // glm::mat4 orig = PCO.Model; // for(auto &pair : iterWorld->second) { // if(auto& voxels = std::get<0>(pair.second)) { // glm::vec3 cpos(pair.first.x, pair.first.y, pair.first.z); // PCO.Model = glm::translate(orig, cpos*16.f); // auto [vkBuffer, offset] = VKCTX->VertexPool_Voxels.map(voxels); // vkCmdPushConstants(drawCmd, MainAtlas_LightMap_PipelineLayout, // VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_GEOMETRY_BIT, offsetof(WorldPCO, Model), sizeof(WorldPCO::Model), &PCO.Model); // vkCmdBindVertexBuffers(drawCmd, 0, 1, &vkBuffer, &vkOffsets); // vkCmdDraw(drawCmd, voxels.VertexCount, 1, offset, 0); // } // } // PCO.Model = orig; // } // } // vkCmdBindPipeline(drawCmd, VK_PIPELINE_BIND_POINT_GRAPHICS, NodeStaticOpaquePipeline); // vkCmdPushConstants(drawCmd, MainAtlas_LightMap_PipelineLayout, // VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_GEOMETRY_BIT, 0, sizeof(WorldPCO), &PCO); // vkCmdBindDescriptorSets(drawCmd, VK_PIPELINE_BIND_POINT_GRAPHICS, // MainAtlas_LightMap_PipelineLayout, 0, 2, // (const VkDescriptorSet[]) {MainAtlasDescriptor, VoxelLightMapDescriptor}, 0, nullptr); // { // auto iterWorld = External.ChunkVoxelMesh.find(WorldId); // if(iterWorld != External.ChunkVoxelMesh.end()) { // glm::mat4 orig = PCO.Model; // for(auto &pair : iterWorld->second) { // if(auto& nodes = std::get<1>(pair.second)) { // glm::vec3 cpos(pair.first.z, pair.first.y, pair.first.x); // PCO.Model = glm::translate(orig, cpos*16.f); // auto [vkBuffer, offset] = VKCTX->VertexPool_Nodes.map(nodes); // vkCmdPushConstants(drawCmd, MainAtlas_LightMap_PipelineLayout, // VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_GEOMETRY_BIT, offsetof(WorldPCO, Model), sizeof(WorldPCO::Model), &PCO.Model); // vkCmdBindVertexBuffers(drawCmd, 0, 1, &vkBuffer, &vkOffsets); // vkCmdDraw(drawCmd, nodes.VertexCount, 1, offset, 0); // } // } // PCO.Model = orig; // } // } static float Delta = 0; Delta += dTime; PCO.Model = glm::mat4(1); //PCO.Model = glm::translate(PCO.Model, -X64Offset_f); // glm::quat quat = glm::inverse(Quat); { // auto *srv = (class ServerSession*) ServerSession; glm::vec4 v = glm::mat4(glm::inverse(Quat))*glm::vec4(0, 0, -6, 1); Pos::GlobalNode pos = (Pos::GlobalNode) (glm::vec3) v; pos += (Pos-X64Offset) >> Pos::Object_t::BS_Bit; PCO.Model = glm::translate(PCO.Model, glm::vec3(pos)); } { glm::mat4 proj = glm::perspective(glm::radians(75.f), float(VkInst->Screen.Width)/float(VkInst->Screen.Height), 0.5, std::pow(2, 17)); proj[1][1] *= -1; // Получили область рендера от левого верхнего угла // x -1 -> 1; y 1 -> -1; z 0 -> -1 // Правило левой руки // Перед полигонов определяется обходом против часовой стрелки glm::mat4 view = glm::mat4(1); // Смещаем мир относительно позиции игрока, чтобы игрок в пространстве рендера оказался в нулевых координатах view = glm::translate(view, -X64Delta); // Поворачиваем мир обратно взгляду игрока, чтобы его взгляд стал по направлению оси -z view = glm::mat4(Quat)*view; // Сначала применяется матрица вида, потом проекции PCO.ProjView = proj*view; } vkCmdBindPipeline(drawCmd, VK_PIPELINE_BIND_POINT_GRAPHICS, NodeStaticOpaquePipeline); vkCmdPushConstants(drawCmd, MainAtlas_LightMap_PipelineLayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_GEOMETRY_BIT, 0, sizeof(WorldPCO), &PCO); vkCmdBindDescriptorSets(drawCmd, VK_PIPELINE_BIND_POINT_GRAPHICS, MainAtlas_LightMap_PipelineLayout, 0, 2, (const VkDescriptorSet[]) {TP ? TP->getDescriptorSet() : VK_NULL_HANDLE, VoxelLightMapDescriptor}, 0, nullptr); { // glm::vec4 offset = glm::inverse(Quat)*glm::vec4(0, 0, -64, 1); // PCO.Model = glm::translate(glm::mat4(1), glm::vec3(offset)); PCO.Model = glm::mat4(1); } VkBuffer vkBuffer = TestQuad; VkDeviceSize vkOffsets = 0; vkCmdBindVertexBuffers(drawCmd, 0, 1, &vkBuffer, &vkOffsets); vkCmdDraw(drawCmd, 6*3*2, 1, 0, 0); { PCO.Model = glm::mat4(1.0f); Pos::GlobalChunk x64offset = X64Offset >> Pos::Object_t::BS_Bit >> 4; Pos::GlobalRegion x64offset_region = x64offset >> 2; auto [voxelVertexs, nodeVertexs] = CP.getChunksForRender(WorldId, Pos, 1, PCO.ProjView, x64offset_region); // { // static uint32_t l = TOS::Time::getSeconds(); // if(l != TOS::Time::getSeconds()) { // l = TOS::Time::getSeconds(); // TOS::Logger("Test").debug() << nodeVertexs.size(); // } // } size_t count = 0; glm::mat4 orig = PCO.Model; for(auto& [chunkPos, vertexs, indexes, type, vertexCount] : nodeVertexs) { count += vertexCount; glm::vec3 cpos(chunkPos-x64offset); PCO.Model = glm::translate(orig, cpos*16.f); auto [vkBufferV, offsetV] = vertexs; auto [vkBufferI, offsetI] = indexes; vkCmdPushConstants(drawCmd, MainAtlas_LightMap_PipelineLayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_GEOMETRY_BIT, offsetof(WorldPCO, Model), sizeof(WorldPCO::Model), &PCO.Model); VkDeviceSize offset = offsetV*sizeof(NodeVertexStatic); vkCmdBindVertexBuffers(drawCmd, 0, 1, &vkBufferV, &offset); offset = offsetI * (type ? 2 : 4); vkCmdBindIndexBuffer(drawCmd, vkBufferI, offset, type ? VK_INDEX_TYPE_UINT16 : VK_INDEX_TYPE_UINT32); vkCmdDrawIndexed(drawCmd, vertexCount, 1, 0, 0, 0); //vkCmdDraw(drawCmd, vertexCount, 1, 0, 0); } PCO.Model = orig; } vkCmdBindPipeline(drawCmd, VK_PIPELINE_BIND_POINT_GRAPHICS, NodeStaticTransparentPipeline); vkCmdPushConstants(drawCmd, MainAtlas_LightMap_PipelineLayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_GEOMETRY_BIT, 0, sizeof(WorldPCO), &PCO); vkCmdBindDescriptorSets(drawCmd, VK_PIPELINE_BIND_POINT_GRAPHICS, MainAtlas_LightMap_PipelineLayout, 0, 2, (const VkDescriptorSet[]) {TP ? TP->getDescriptorSet() : VK_NULL_HANDLE, VoxelLightMapDescriptor}, 0, nullptr); ensureAtlasLayerPreview(); if(AtlasLayersPreview) { glm::mat4 previewModel = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 64.0f, 0.0f)-glm::vec3(X64Offset >> Pos::Object_t::BS_Bit)); vkCmdPushConstants(drawCmd, MainAtlas_LightMap_PipelineLayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_GEOMETRY_BIT, offsetof(WorldPCO, Model), sizeof(WorldPCO::Model), &previewModel); VkBuffer previewBuffer = *AtlasLayersPreview; VkDeviceSize previewOffset = 0; vkCmdBindVertexBuffers(drawCmd, 0, 1, &previewBuffer, &previewOffset); vkCmdDraw(drawCmd, AtlasLayersPreviewCount * 6, 1, 0, 0); } if(false) { ensureEntityTexture(); if(!ServerSession->Content.Entityes.empty()) { VkBuffer entityBuffer = TestQuad; VkDeviceSize entityOffset = 0; vkCmdBindVertexBuffers(drawCmd, 0, 1, &entityBuffer, &entityOffset); glm::mat4 orig = PCO.Model; for(const auto& pair : ServerSession->Content.Entityes) { const auto& info = pair.second; if(info.WorldId != WorldId) continue; glm::vec3 entityPos = Pos::Object_t::asFloatVec(info.Pos - X64Offset); entityPos.y -= 1.6f; // Camera position arrives as eye height. glm::mat4 model = glm::translate(glm::mat4(1.0f), entityPos); model = model * glm::mat4(info.Quat); model = glm::scale(model, glm::vec3(0.6f, 1.8f, 0.6f)); PCO.Model = model; vkCmdPushConstants(drawCmd, MainAtlas_LightMap_PipelineLayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_GEOMETRY_BIT, offsetof(WorldPCO, Model), sizeof(WorldPCO::Model), &PCO.Model); vkCmdDraw(drawCmd, 6*3*2, 1, 0, 0); } PCO.Model = orig; } } CP.pushFrame(); } void VulkanRenderSession::updateTestQuadTexture(uint32_t texId) { if(EntityTextureReady && EntityTextureId == texId) return; auto *array = reinterpret_cast(TestQuad.mapMemory()); const size_t vertexCount = TestQuad.getSize() / sizeof(NodeVertexStatic); for(size_t iter = 0; iter < vertexCount; ++iter) array[iter].Tex = texId; TestQuad.unMapMemory(); EntityTextureId = texId; EntityTextureReady = true; } void VulkanRenderSession::ensureEntityTexture() { if(EntityTextureReady || !TP || !NSP) return; auto iter = ServerSession->Assets.find(EnumAssets::Texture); if(iter == ServerSession->Assets.end() || iter->second.empty()) return; const AssetEntry* picked = nullptr; for(const auto& [id, entry] : iter->second) { if(entry.Key == "default.png") { picked = &entry; break; } } if(!picked) { for(const auto& [id, entry] : iter->second) { if(entry.Key == "grass.png") { picked = &entry; break; } } } if(!picked) picked = &iter->second.begin()->second; updateTestQuadTexture(NSP->getTextureId(picked->Id)); } void VulkanRenderSession::ensureAtlasLayerPreview() { if(!TP) return; const uint32_t maxLayers = TP->getAtlasMaxLayers(); if(maxLayers == 0) return; if(AtlasLayersPreview && AtlasLayersPreviewCount == maxLayers) return; TP->requestAtlasLayerCount(maxLayers); const uint32_t vertsPerQuad = 6; const uint32_t totalVerts = maxLayers * vertsPerQuad; std::vector verts(totalVerts); const uint32_t columns = 4; const uint32_t base = 224; const uint32_t step = 320; const uint32_t size = 256; const uint32_t z = base; auto makeVert = [&](uint32_t fx, uint32_t fy, uint32_t tex, uint32_t tu, uint32_t tv) -> NodeVertexStatic { NodeVertexStatic v{}; v.FX = fx; v.FY = fy; v.FZ = z; v.LS = 0; v.Tex = tex; v.TU = tu; v.TV = tv; return v; }; for(uint32_t layer = 0; layer < maxLayers; ++layer) { const uint32_t col = layer % columns; const uint32_t row = layer / columns; const uint32_t x0 = base + col * step; const uint32_t y0 = base + row * step; const uint32_t x1 = x0 + size; const uint32_t y1 = y0 + size; const uint32_t tex = TP->getAtlasLayerId(layer); const size_t start = static_cast(layer) * vertsPerQuad; verts[start + 0] = makeVert(x0, y0, tex, 0, 0); verts[start + 1] = makeVert(x0, y1, tex, 0, 65535); verts[start + 2] = makeVert(x1, y1, tex, 65535, 65535); verts[start + 3] = makeVert(x0, y0, tex, 0, 0); verts[start + 4] = makeVert(x1, y1, tex, 65535, 65535); verts[start + 5] = makeVert(x1, y0, tex, 65535, 0); } AtlasLayersPreview.emplace(VkInst, verts.size() * sizeof(NodeVertexStatic)); std::memcpy(AtlasLayersPreview->mapMemory(), verts.data(), verts.size() * sizeof(NodeVertexStatic)); AtlasLayersPreview->unMapMemory(); AtlasLayersPreviewCount = maxLayers; } void VulkanRenderSession::pushStage(EnumRenderStage stage) { } std::vector VulkanRenderSession::generateMeshForVoxelChunks(const std::vector& cubes) { std::vector out; out.reserve(cubes.size()*6); for(const VoxelCube &cube : cubes) { out.emplace_back( cube.Pos.x, cube.Pos.y, cube.Pos.z, 0, 0, 0, cube.Size.x, cube.Size.z, cube.VoxelId, 0, 0, 0 ); out.emplace_back( cube.Pos.x, cube.Pos.y, cube.Pos.z, 1, 0, 0, cube.Size.x, cube.Size.y, cube.VoxelId, 0, 0, 0 ); out.emplace_back( cube.Pos.x, cube.Pos.y, cube.Pos.z, 2, 0, 0, cube.Size.z, cube.Size.y, cube.VoxelId, 0, 0, 0 ); out.emplace_back( cube.Pos.x, cube.Pos.y+cube.Size.y+1, cube.Pos.z, 3, 0, 0, cube.Size.x, cube.Size.z, cube.VoxelId, 0, 0, 0 ); out.emplace_back( cube.Pos.x, cube.Pos.y, cube.Pos.z+cube.Size.z+1, 4, 0, 0, cube.Size.x, cube.Size.y, cube.VoxelId, 0, 0, 0 ); out.emplace_back( cube.Pos.x+cube.Size.x+1, cube.Pos.y, cube.Pos.z, 5, 0, 0, cube.Size.z, cube.Size.y, cube.VoxelId, 0, 0, 0 ); } return out; } void VulkanRenderSession::updateDescriptor_VoxelsLight() { VkDescriptorBufferInfo bufferInfo = LightDummy; VkDescriptorImageInfo imageInfo = LightDummy; std::vector ciDescriptorSet = { { .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, .pNext = nullptr, .dstSet = VoxelLightMapDescriptor, .dstBinding = 0, .dstArrayElement = 0, .descriptorCount = 1, .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, .pImageInfo = &imageInfo }, { .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, .pNext = nullptr, .dstSet = VoxelLightMapDescriptor, .dstBinding = 1, .dstArrayElement = 0, .descriptorCount = 1, .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, .pBufferInfo = &bufferInfo } }; vkUpdateDescriptorSets(VkInst->Graphics.Device, ciDescriptorSet.size(), ciDescriptorSet.data(), 0, nullptr); } void VulkanRenderSession::updateDescriptor_ChunksLight() { } }