Files
LuaVox/Src/Client/Vulkan/VulkanRenderSession.cpp

2118 lines
82 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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 <atomic>
#include <algorithm>
#include <chrono>
#include <cstddef>
#include <cstring>
#include <memory>
#include <string>
#include <thread>
#include <unordered_map>
#include <vector>
#include <vulkan/vulkan_core.h>
#include <fstream>
namespace std {
template<>
struct hash<LV::Client::VK::NodeVertexStatic> {
size_t operator()(const LV::Client::VK::NodeVertexStatic& v) const {
const uint32_t* ptr = reinterpret_cast<const uint32_t*>(&v);
size_t h1 = std::hash<uint32_t>{}(ptr[0]);
size_t h2 = std::hash<uint32_t>{}(ptr[1]);
size_t h3 = std::hash<uint32_t>{}(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<WorldId_t, Pos::GlobalChunk, uint32_t> 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<Node, 16*16*16>* chunk;
const std::vector<VoxelCube>* voxels;
// Если на позиции полная нода, то она перекрывает стороны соседей
uint8_t fullNodes[18][18][18];
// Профиль, который используется если на стороне клиента отсутствует нужных профиль
DefNode defaultProfileNode;
// Кеш запросов профилей нод
std::unordered_map<DefNodeId, const DefNode*> profilesNodeCache;
auto getNodeProfile = [&](DefNodeId id) -> const DefNode* {
auto iterCache = profilesNodeCache.find(id);
if(iterCache == profilesNodeCache.end()) {
// Промах кеша
auto iterSS = SS->Profiles.DefNodes.find(id);
if(iterSS != SS->Profiles.DefNodes.end()) {
return (profilesNodeCache[id] = &iterSS->second);
} else {
// Профиль отсутствует на клиенте
return (profilesNodeCache[id] = &defaultProfileNode);
}
} else {
return iterCache->second;
}
};
std::vector<NodeStateInfo> metaStatesInfo;
{
NodeStateInfo info;
info.Name = "meta";
info.Variations = 256;
metaStatesInfo.push_back(std::move(info));
}
// Воксели пока не рендерим
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<Node, 16*16*16>* 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<DefNodeId, bool> nodeFullCuboidCache;
auto nodeIsFull = [&](Node node) -> bool {
if(node.NodeId == 0)
return false;
auto iterCache = nodeFullCuboidCache.find(node.Data);
if(iterCache == nodeFullCuboidCache.end()) {
const DefNode* profile = getNodeProfile(node.NodeId);
if(NSP) {
if(const AssetsNodestate* ptr = std::get_if<AssetsNodestate>(&profile->RenderStates)) {
if(NSP->hasNodestate(*ptr)) {
std::unordered_map<std::string, int32_t> states;
int32_t meta = node.Meta;
states.emplace("meta", meta);
const auto routes = NSP->getModelsForNode(*ptr, metaStatesInfo, states);
bool isFull = !routes.empty();
if(isFull) {
for(const auto& variants : routes) {
for(const auto& [weight, faces] : variants) {
(void)weight;
auto hasFace = [&](EnumFace face) -> bool {
auto iterFace = faces.find(face);
return iterFace != faces.end() && !iterFace->second.empty();
};
if(!hasFace(EnumFace::Up)
|| !hasFace(EnumFace::Down)
|| !hasFace(EnumFace::East)
|| !hasFace(EnumFace::West)
|| !hasFace(EnumFace::South)
|| !hasFace(EnumFace::North))
{
isFull = false;
break;
}
}
if(!isFull)
break;
}
}
return (nodeFullCuboidCache[node.Data] = isFull);
}
}
}
return (nodeFullCuboidCache[node.Data] = true);
} 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<uint32_t> debugMeshWarnCount = 0;
const bool debugMeshEnabled = debugMeshWarnCount.load() < 16;
std::array<uint8_t, 16> expectedColumnX = {};
std::array<uint8_t, 16> expectedColumnY = {};
std::array<uint8_t, 16> expectedColumnZ = {};
std::array<uint8_t, 16> generatedColumnX = {};
std::array<uint8_t, 16> generatedColumnY = {};
std::array<uint8_t, 16> generatedColumnZ = {};
struct ModelCacheEntry {
std::vector<std::vector<std::pair<float, std::unordered_map<EnumFace, std::vector<NodeVertexStatic>>>>> Routes;
};
std::unordered_map<uint32_t, ModelCacheEntry> modelCache;
std::unordered_map<AssetsTexture, uint32_t> baseTextureCache;
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<std::pair<float, std::unordered_map<EnumFace, std::vector<NodeVertexStatic>>>>& variants, uint32_t seed)
-> const std::unordered_map<EnumFace, std::vector<NodeVertexStatic>>*
{
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<EnumFace, std::vector<NodeVertexStatic>>& 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 Node& nodeData = (*chunk)[x+y*16+z*16*16];
if(nodeData.NodeId == 0)
continue;
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 DefNode* node = getNodeProfile(nodeData.NodeId);
bool usedModel = false;
if(NSP) {
if(const AssetsNodestate* ptr = std::get_if<AssetsNodestate>(&node->RenderStates)) {
if(NSP->hasNodestate(*ptr)) {
auto iterCache = modelCache.find(nodeData.Data);
if(iterCache == modelCache.end()) {
std::unordered_map<std::string, int32_t> states;
states.emplace("meta", nodeData.Meta);
ModelCacheEntry entry;
entry.Routes = NSP->getModelsForNode(*ptr, 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;
v.Tex = 0;
// Рендерим обычный кубоид
// 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:
}
// Вычислить индексы и сократить вершины
{
uint32_t nextIndex = 0;
std::vector<NodeVertexStatic> vertexes;
std::unordered_map<NodeVertexStatic, uint32_t> vertexTable;
std::vector<uint32_t> 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<uint16_t> 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<WorldId_t, std::vector<Pos::GlobalChunk>> changedChunks = data.ChangedChunks;
if(!data.ChangedNodes.empty()) {
std::unordered_set<DefNodeId> 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<DefVoxelId> 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<std::tuple<WorldId_t, Pos::GlobalChunk, uint32_t>> toBuild;
for(auto& [wId, chunks] : changedChunks) {
std::vector<Pos::GlobalChunk> 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<ChunkMeshGenerator::ChunkObj_t> 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<uint16_t>* ptr = std::get_if<std::vector<uint16_t>>(&chunk.NodeIndexes)) {
if(!ptr->empty())
rChunk.NodeIndexes = IndexPool_Nodes_16.pushVertexs(std::move(*ptr));
} else if(std::vector<uint32_t>* ptr = std::get_if<std::vector<uint32_t>>(&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<uint16_t>::Pointer* ind = std::get_if<IndexPool<uint16_t>::Pointer>(&std::get<1>(pointer))) {
IndexPool_Nodes_16.dropVertexs(*ind);
} else if(IndexPool<uint32_t>::Pointer* ind = std::get_if<IndexPool<uint32_t>::Pointer>(&std::get<1>(pointer))) {
IndexPool_Nodes_32.dropVertexs(*ind);
}
}
VPN_ToFree[FrameRoulette].clear();
}
std::pair<
std::vector<std::tuple<Pos::GlobalChunk, std::pair<VkBuffer, int>, uint32_t>>,
std::vector<std::tuple<Pos::GlobalChunk, std::pair<VkBuffer, int>, std::pair<VkBuffer, int>, 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<std::tuple<float, Pos::GlobalChunk, std::pair<VkBuffer, int>, uint32_t>> vertexVoxels;
std::vector<std::tuple<float, Pos::GlobalChunk, std::pair<VkBuffer, int>, std::pair<VkBuffer, int>, 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<uint32_t>([](const auto& val) -> uint32_t { return val.VertexCount; }, chunk.NodeIndexes));
}
}
}
}
}
{
auto sortByDistance = []
(
const std::tuple<float, Pos::GlobalChunk, std::pair<VkBuffer, int>, uint32_t>& a,
const std::tuple<float, Pos::GlobalChunk, std::pair<VkBuffer, int>, uint32_t>& b
) {
return std::get<0>(a) < std::get<0>(b);
};
std::sort(vertexVoxels.begin(), vertexVoxels.end(), sortByDistance);
}
{
auto sortByDistance = []
(
const std::tuple<float, Pos::GlobalChunk, std::pair<VkBuffer, int>, std::pair<VkBuffer, int>, bool, uint32_t>& a,
const std::tuple<float, Pos::GlobalChunk, std::pair<VkBuffer, int>, std::pair<VkBuffer, int>, bool, uint32_t>& b
) {
return std::get<0>(a) < std::get<0>(b);
};
std::sort(vertexNodes.begin(), vertexNodes.end(), sortByDistance);
}
std::vector<std::tuple<Pos::GlobalChunk, std::pair<VkBuffer, int>, uint32_t>> resVertexVoxels;
std::vector<std::tuple<Pos::GlobalChunk, std::pair<VkBuffer, int>, std::pair<VkBuffer, int>, 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<VkDescriptorPoolSize> 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<TextureProvider>(VkInst, DescriptorPool);
NSP = std::make_unique<NodestateProvider>(MP, *TP);
CP.setNodestateProvider(NSP.get());
{
std::vector<VkDescriptorSetLayoutBinding> 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<VoxelCube> 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<VoxelVertexPoint> 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<VkPushConstantRange> worldWideShaderPushConstants =
{
{
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_GEOMETRY_BIT,
.offset = 0,
.size = uint32_t(sizeof(WorldPCO))
}
};
std::vector<VkDescriptorSetLayout> 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<VkPipelineShaderStageCreateInfo> 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<VkVertexInputBindingDescription> shaderVertexBindings =
{
{
.binding = 0,
.stride = sizeof(VoxelVertexPoint),
.inputRate = VK_VERTEX_INPUT_RATE_VERTEX
}
};
std::vector<VkVertexInputAttributeDescription> 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<VkPipelineColorBlendAttachmentState> 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<VkDynamicState> 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(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<AssetsModel> changedModels;
if(!data.AssetsModels.empty())
changedModels = MP.onModelChanges(std::move(data.AssetsModels));
if(TP && !data.AssetsTextures.empty())
TP->onTexturesChanges(std::move(data.AssetsTextures));
std::vector<AssetsNodestate> changedNodestates;
if(NSP && (!data.AssetsNodestates.empty() || !changedModels.empty())) {
changedNodestates = NSP->onNodestateChanges(std::move(data.AssetsNodestates), std::move(changedModels));
}
if(!changedNodestates.empty()) {
std::unordered_set<AssetsNodestate> changed;
changed.reserve(changedNodestates.size());
for(AssetsNodestate id : changedNodestates)
changed.insert(id);
for(const auto& [nodeId, def] : ServerSession->Profiles.DefNodes) {
if(const AssetsNodestate* ptr = std::get_if<AssetsNodestate>(&def.RenderStates))
if(changed.contains(*ptr))
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<float>()/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<float>(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<NodeVertexStatic*>(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;
return;
}
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<NodeVertexStatic> 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<size_t>(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<VoxelVertexPoint> VulkanRenderSession::generateMeshForVoxelChunks(const std::vector<VoxelCube>& cubes) {
std::vector<VoxelVertexPoint> 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<VkWriteDescriptorSet> 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() {
}
}