#pragma once #include "Client/Abstract.hpp" #include "Common/Abstract.hpp" #include #include #include #include #include #include #include #include #include "Abstract.hpp" #include "TOSLib.hpp" #include "VertexPool.hpp" /* У движка есть один текстурный атлас VK_IMAGE_VIEW_TYPE_2D_ARRAY(RGBA_UINT) и к нему Storage с инфой о положении текстур Это общий для всех VkDescriptorSetLayout и VkDescriptorSet Для отрисовки вокселей используется карта освещения VK_IMAGE_VIEW_TYPE_2D_ARRAY(RGB_UINT), разделённая по прямоугольным плоскостям Разрешение у этих карт 1м/16 -- Для всего остального 3д карты освещённости по чанкам в разрешении 1м. 16^3 = 4096 текселей -- */ /* Самые критичные случаи Для вокселей это чередование в шахматном порядке присутствия и отсутствия вокселей Это 8'388'608 вокселя в чанке, общей площадью на картах освещения (256^3/2 *4 *6) 201'326'592 текселей или текстура размером 16к^2 Понадобится переиспользование одинаковых участков освещённости Если чанк заполнен слоями вокселей в шахматном порядке по вертикале ((257^2+257*2*4)*128) 8'717'440 текселей или текстура размером 4к^2 */ namespace LV::Client::VK { struct WorldPCO { glm::mat4 ProjView, Model; }; static_assert(sizeof(WorldPCO) == 128); /* Модуль, рисующий то, что предоставляет IServerSession */ class VulkanRenderSession : public IRenderSession, public IVulkanDependent { VK::Vulkan *VkInst = nullptr; // Доступ к миру на стороне клиента IServerSession *ServerSession = nullptr; // Положение камеры WorldId_t WorldId; Pos::Object Pos; /* Графический конвейер оперирует числами с плавающей запятой Для сохранения точности матрица модели хранит смещения близкие к нулю (X64Delta) глобальные смещения на уровне региона исключаются из смещения ещё при задании матрицы модели X64Offset = позиция игрока на уровне регионов X64Delta = позиция игрока в рамках региона Внутри графического конвейера будут числа приблежённые к 0 */ // Смещение дочерних объекто на стороне хоста перед рендером Pos::Object X64Offset; glm::vec3 X64Offset_f, X64Delta; // Смещение мира относительно игрока в матрице вида (0 -> 64) glm::quat Quat; /* Поток, занимающийся генерацией меша на основе нод и вокселей Требует доступ к профилям в ServerSession (ServerSession должен быть заблокирован только на чтение) Также доступ к идентификаторам текстур в VulkanRenderSession (только на чтение) Должен оповещаться об изменениях профилей и событий чанков Удалённые мешы хранятся в памяти N количество кадров */ struct ThreadVertexObj_t { // Здесь не хватает стадии работы с текстурами enum class EnumStage { // Постройка буфера команд на рисовку // В этот период не должно быть изменений в таблице, // хранящей указатели на данные для рендера ChunkMesh // Можно работать с миром // Здесь нужно дождаться завершения работы с ChunkMesh ComposingCommandBuffer, // В этот период можно менять ChunkMesh // Можно работать с миром Render, // В этот период нельзя работать с миром // Можно менять ChunkMesh // Здесь нужно дождаться завершения работы с миром, только в // этом этапе могут приходить события изменения чанков и определений WorldUpdate, Shutdown } Stage = EnumStage::Render; volatile bool ChunkMesh_IsUse = false, ServerSession_InUse = false; struct ChunkObj_t { // Сортированный список уникальных значений std::vector VoxelDefines; VertexPool::Pointer VoxelPointer; std::vector NodeDefines; VertexPool::Pointer NodePointer; }; std::unordered_map> > ChunkMesh; ThreadVertexObj_t(Vulkan* vkInst) : VertexPool_Voxels(vkInst), VertexPool_Nodes(vkInst), Thread(&ThreadVertexObj_t::run, this) {} ~ThreadVertexObj_t() { Stage = EnumStage::Shutdown; Thread.join(); } // Сюда входят добавленные/изменённые/удалённые определения нод и вокселей // Чтобы перерисовать чанки, связанные с ними void onContentDefinesChange(const std::vector& voxels, const std::vector& nodes) { ChangedDefines_Voxel.insert(ChangedDefines_Voxel.end(), voxels.begin(), voxels.end()); ChangedDefines_Node.insert(ChangedDefines_Node.end(), nodes.begin(), nodes.end()); } // Изменение/удаление чанков void onContentChunkChange(const std::unordered_map>& chunkChanges, const std::unordered_map>& regionRemove) { for(auto& [worldId, chunks] : chunkChanges) { auto &list = ChangedContent_Chunk[worldId]; list.insert(list.end(), chunks.begin(), chunks.end()); } for(auto& [worldId, regions] : regionRemove) { auto &list = ChangedContent_RegionRemove[worldId]; list.insert(list.end(), regions.begin(), regions.end()); } } // Синхронизация потока рендера мира void pushStage(EnumStage stage) { if(Stage == EnumStage::Shutdown) MAKE_ERROR("Остановка из-за ошибки ThreadVertex"); assert(Stage != stage); Stage = stage; if(stage == EnumStage::ComposingCommandBuffer) { while(ChunkMesh_IsUse); } else if(stage == EnumStage::WorldUpdate) { while(ServerSession_InUse); } } private: // Буферы для хранения вершин VertexPool VertexPool_Voxels; VertexPool VertexPool_Nodes; // Списки изменённых определений std::vector ChangedDefines_Voxel; std::vector ChangedDefines_Node; // Список чанков на перерисовку std::unordered_map> ChangedContent_Chunk; std::unordered_map> ChangedContent_RegionRemove; // Внешний поток std::thread Thread; void run(); }; struct VulkanContext { VK::Vulkan *VkInst; AtlasImage MainTest, LightDummy; Buffer TestQuad; std::optional TestVoxel; ThreadVertexObj_t ThreadVertexObj; VulkanContext(Vulkan* vkInst) : VkInst(vkInst), MainTest(vkInst), LightDummy(vkInst), TestQuad(vkInst, sizeof(NodeVertexStatic)*6*3*2), ThreadVertexObj(vkInst) {} ~VulkanContext() { ThreadVertexObj.pushStage(ThreadVertexObj_t::EnumStage::Shutdown); } void onUpdate(); }; std::shared_ptr VKCTX; VkDescriptorPool DescriptorPool = VK_NULL_HANDLE; /* .binding = 0, .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, Текстурный атлас .binding = 1, .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, Данные к атласу */ VkDescriptorSetLayout MainAtlasDescLayout = VK_NULL_HANDLE; VkDescriptorSet MainAtlasDescriptor = VK_NULL_HANDLE; /* .binding = 2, .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, Воксельная карта освещения .binding = 3, .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, Информация о размерах карты для приведения размеров */ VkDescriptorSetLayout VoxelLightMapDescLayout = VK_NULL_HANDLE; VkDescriptorSet VoxelLightMapDescriptor = VK_NULL_HANDLE; // Для отрисовки с использованием текстурного атласа и карты освещения VkPipelineLayout MainAtlas_LightMap_PipelineLayout = VK_NULL_HANDLE; // Для отрисовки вокселей std::shared_ptr VoxelShaderVertex, VoxelShaderGeometry, VoxelShaderFragmentOpaque, VoxelShaderFragmentTransparent; VkPipeline VoxelOpaquePipeline = VK_NULL_HANDLE, // Альфа канал может быть либо 255, либо 0 VoxelTransparentPipeline = VK_NULL_HANDLE; // Допускается полупрозрачность и смешивание // Для отрисовки статичных, не анимированных нод std::shared_ptr NodeShaderVertex, NodeShaderGeometry, NodeShaderFragmentOpaque, NodeShaderFragmentTransparent; VkPipeline NodeStaticOpaquePipeline = VK_NULL_HANDLE, NodeStaticTransparentPipeline = VK_NULL_HANDLE; std::map ServerToAtlas; struct { } External; virtual void free(Vulkan *instance) override; virtual void init(Vulkan *instance) override; public: WorldPCO PCO; public: VulkanRenderSession(); virtual ~VulkanRenderSession(); void setServerSession(IServerSession *serverSession) { ServerSession = serverSession; assert(serverSession); } virtual void onBinaryResourceAdd(std::vector) override; virtual void onContentDefinesAdd(std::unordered_map>) override; virtual void onContentDefinesLost(std::unordered_map>) override; virtual void onChunksChange(WorldId_t worldId, const std::unordered_set& changeOrAddList, const std::unordered_set& remove) override; virtual void setCameraPos(WorldId_t worldId, Pos::Object pos, glm::quat quat) override; glm::mat4 calcViewMatrix(glm::quat quat, glm::vec3 camOffset = glm::vec3(0)) { return glm::translate(glm::mat4(quat), camOffset); } void beforeDraw(); void drawWorld(GlobalTime gTime, float dTime, VkCommandBuffer drawCmd); static std::vector generateMeshForVoxelChunks(const std::vector cubes); static std::vector generateMeshForNodeChunks(const Node* nodes); private: void updateDescriptor_MainAtlas(); void updateDescriptor_VoxelsLight(); void updateDescriptor_ChunksLight(); }; }