diff --git a/Src/Client/Abstract.hpp b/Src/Client/Abstract.hpp index b0e522b..dc96080 100644 --- a/Src/Client/Abstract.hpp +++ b/Src/Client/Abstract.hpp @@ -9,6 +9,26 @@ namespace LV::Client { +struct GlobalTime { + uint32_t Seconds : 22, Sub : 10; + + GlobalTime() = default; + GlobalTime(double gTime) { + Seconds = int(gTime); + Sub = (gTime-int(gTime))*1024; + } + + GlobalTime& operator=(double gTime) { + Seconds = int(gTime); + Sub = (gTime-int(gTime))*1024; + return *this; + } + + operator double() const { + return double(Seconds) + double(Sub)/1024.; + } +}; + struct VoxelCube { DefVoxelId_c VoxelId; Pos::Local256 Left, Right; @@ -129,7 +149,7 @@ public: virtual ~IServerSession(); - virtual void atFreeDrawTime() = 0; + virtual void atFreeDrawTime(GlobalTime gTime, float dTime) = 0; }; diff --git a/Src/Client/ServerSession.cpp b/Src/Client/ServerSession.cpp index 041aba0..d173c4f 100644 --- a/Src/Client/ServerSession.cpp +++ b/Src/Client/ServerSession.cpp @@ -2,13 +2,13 @@ #include "Client/Abstract.hpp" #include "Common/Abstract.hpp" #include "Common/Net.hpp" +#include #include #include #include #include #include #include -#include #include @@ -162,17 +162,24 @@ void ServerSession::onCursorPosChange(int32_t width, int32_t height) { } void ServerSession::onCursorMove(float xMove, float yMove) { + glm::vec3 deltaPYR; + + static constexpr float PI = glm::pi(), PI2 = PI*2, PI_HALF = PI/2, PI_DEG = PI/180; + + deltaPYR.x = std::clamp(PYR.x + yMove*PI_DEG, -PI_HALF+PI_DEG, PI_HALF-PI_DEG)-PYR.x; + deltaPYR.y = std::fmod(PYR.y + xMove*PI_DEG, PI2)-PYR.y; + deltaPYR.z = 0; + + double gTime = GTime; + float deltaTime = 1-std::min(gTime-PYR_At, 1/PYR_TIME_DELTA)*PYR_TIME_DELTA; + PYR_At = GTime; + + PYR += deltaPYR; + PYR_Offset = deltaPYR+deltaTime*PYR_Offset; + glm::vec3 front = Camera.Quat*glm::vec3(0.0f, 0.0f, -1.0f); } -void ServerSession::onFrameRendering() { - -} - -void ServerSession::onFrameRenderEnd() { - -} - void ServerSession::onCursorBtn(ISurfaceEventListener::EnumCursorBtn btn, bool state) { } @@ -187,8 +194,8 @@ void ServerSession::onJoystick() { } -void ServerSession::atFreeDrawTime() { - +void ServerSession::atFreeDrawTime(GlobalTime gTime, float dTime) { + GTime = gTime; } coro<> ServerSession::run() { diff --git a/Src/Client/ServerSession.hpp b/Src/Client/ServerSession.hpp index c15cd4b..4f19fe5 100644 --- a/Src/Client/ServerSession.hpp +++ b/Src/Client/ServerSession.hpp @@ -37,6 +37,12 @@ class ServerSession : public AsyncObject, public IServerSession, public ISurface boost::lockfree::spsc_queue> NetInputPackets; + // + glm::vec3 PYR = glm::vec3(0), PYR_Offset = glm::vec3(0); + double PYR_At = 0; + static constexpr float PYR_TIME_DELTA = 30; + GlobalTime GTime; + public: // Нужен сокет, на котором только что был согласован игровой протокол (asyncInitGameProtocol) ServerSession(asio::io_context &ioc, std::unique_ptr &&socket, IRenderSession *rs = nullptr) @@ -70,14 +76,12 @@ public: virtual void onChangeFocusState(bool isFocused) override; virtual void onCursorPosChange(int32_t width, int32_t height) override; virtual void onCursorMove(float xMove, float yMove) override; - virtual void onFrameRendering() override; - virtual void onFrameRenderEnd() override; virtual void onCursorBtn(EnumCursorBtn btn, bool state) override; virtual void onKeyboardBtn(int btn, int state) override; virtual void onJoystick() override; - virtual void atFreeDrawTime() override; + virtual void atFreeDrawTime(GlobalTime gTime, float dTime) override; private: coro<> run(); diff --git a/Src/Client/Vulkan/Vulkan.cpp b/Src/Client/Vulkan/Vulkan.cpp index c6af639..f0c6e71 100644 --- a/Src/Client/Vulkan/Vulkan.cpp +++ b/Src/Client/Vulkan/Vulkan.cpp @@ -87,17 +87,6 @@ Vulkan::Vulkan(asio::io_context &ioc) auto useLock = Game.UseLock.lock(); run(); GuardLock.reset(); - - if(Game.Session) { - Game.Session->shutdown(EnumDisconnect::ByInterface); - Game.Session = nullptr; - Game.RSession = nullptr; - } - - if(Game.Server) { - Game.Server->GS.shutdown("Завершение работы из-за остановки клиента"); - Game.Server = nullptr; - } }); } @@ -132,8 +121,14 @@ void Vulkan::run() assert(!vkCreateSemaphore(Graphics.Device, &semaphoreCreateInfo, NULL, &SemaphoreDrawComplete)); - while(!NeedShutdown) + double prevTime = glfwGetTime(); + while(!NeedShutdown + || (Game.Session && Game.Session->isConnected()) + || (Game.Server && Game.Server->GS.isAlive())) { + float dTime = glfwGetTime()-prevTime; + prevTime = glfwGetTime(); + Screen.State = DrawState::Begin; { std::lock_guard lock(Screen.BeforeDrawMtx); @@ -144,8 +139,16 @@ void Vulkan::run() } } - if(glfwWindowShouldClose(Graphics.Window)) { + if(!NeedShutdown && glfwWindowShouldClose(Graphics.Window)) { NeedShutdown = true; + + if(Game.Session) { + Game.Session->shutdown(EnumDisconnect::ByInterface); + } + + if(Game.Server) { + Game.Server->GS.shutdown("Завершение работы из-за остановки клиента"); + } } if(Game.Session) { @@ -168,6 +171,10 @@ void Vulkan::run() // if(CallBeforeDraw) // CallBeforeDraw(this); + if(Game.RSession) { + Game.RSession->beforeDraw(); + } + glfwPollEvents(); VkResult err; @@ -251,8 +258,17 @@ void Vulkan::run() vkCmdSetScissor(Graphics.CommandBufferRender, 0, 1, &scissor); } - // if(CallOnDraw) - // CallOnDraw(this, 0, Graphics.CommandBufferRender); + GlobalTime gTime = glfwGetTime(); + + if(Game.RSession) { + auto &robj = *Game.RSession; + // Рендер мира + + robj.drawWorld(gTime, dTime, Graphics.CommandBufferRender); + + uint16_t minSize = std::min(Screen.Width, Screen.Height); + glm::ivec2 interfaceSize = {int(Screen.Width*720/minSize), int(Screen.Height*720/minSize)}; + } vkCmdEndRenderPass(Graphics.CommandBufferRender); @@ -400,6 +416,10 @@ void Vulkan::run() assert(!err); } + if(Game.Session) { + Game.Session->atFreeDrawTime(gTime, dTime); + } + assert(!vkQueueWaitIdle(Graphics.DeviceQueueGraphic)); vkDeviceWaitIdle(Graphics.Device); @@ -410,6 +430,10 @@ void Vulkan::run() vkDestroySemaphore(Graphics.Device, SemaphoreDrawComplete, nullptr); Graphics.ThisThread = std::thread::id(); + + Game.Session = nullptr; + Game.RSession = nullptr; + Game.Server = nullptr; } void Vulkan::glfwCallbackError(int error, const char *description) @@ -1308,7 +1332,7 @@ void Vulkan::initNextSettings() } else { for(const std::shared_ptr &dependent : ROS_Dependents) { - if(dependent->needRebuild() != EnumRebuildType::None || dynamic_cast(dependent.get())) + if(/*dependent->needRebuild() != EnumRebuildType::None || */ dynamic_cast(dependent.get())) dependent->free(this); } @@ -1842,6 +1866,7 @@ bool Vulkan::needFullVulkanRebuild() std::shared_ptr Vulkan::createShader(const ByteBuffer &data) { assert(Graphics.Device); + assert(data.size()); std::shared_ptr module = std::make_shared(data); std::dynamic_pointer_cast(module)->init(this); ROS_Dependents.insert(module); @@ -1967,7 +1992,8 @@ void Vulkan::gui_MainMenu() { if(ConnectionProgress.Socket) { std::unique_ptr sock = std::move(ConnectionProgress.Socket); - Game.RSession = std::make_unique(this); + Game.RSession = std::make_unique(); + *this << Game.RSession; Game.Session = std::make_unique(IOC, std::move(sock), Game.RSession.get()); Game.RSession->setServerSession(Game.Session.get()); Game.ImGuiInterfaces.push_back(&Vulkan::gui_ConnectedToServer); @@ -1989,19 +2015,18 @@ void Vulkan::gui_ConnectedToServer() { } -EnumRebuildType IVulkanDependent::needRebuild() { return EnumRebuildType::None; } IVulkanDependent::~IVulkanDependent() = default; void Vulkan::updateResources() { - for(const std::shared_ptr &dependent : ROS_Dependents) - { - if(dependent->needRebuild() != EnumRebuildType::None) - { - dependent->free(this); - dependent->init(this); - } - } + // for(const std::shared_ptr &dependent : ROS_Dependents) + // { + // if(dependent->needRebuild() != EnumRebuildType::None) + // { + // dependent->free(this); + // dependent->init(this); + // } + // } } @@ -2412,11 +2437,6 @@ void Pipeline::init(Vulkan *instance) assert(!vkCreateGraphicsPipelines(instance->Graphics.Device, VK_NULL_HANDLE, 1, &pipeline, nullptr, &PipelineObj)); } -EnumRebuildType Pipeline::needRebuild() -{ - return PipelineObj ? EnumRebuildType::None : EnumRebuildType::Urgently; -} - // Shader @@ -4418,4 +4438,115 @@ FontAtlas::GlyphInfo FontAtlas::getGlyph(UChar wc, uint16_t size) return CharToInfo[glyphId] = info; } + + +PipelineVF::PipelineVF(std::shared_ptr layout, const std::string &vertex, + const std::string &fragment) + : Pipeline(layout), PathVertex(vertex), PathFragment(fragment) +{ +} + +PipelineVF::~PipelineVF() = default; + +void PipelineVF::init(Vulkan *instance) +{ + if(!ShaderVertex) + ShaderVertex = instance->createShaderFromFile(PathVertex); + + if(!ShaderFragment) + ShaderFragment = instance->createShaderFromFile(PathFragment); + + Settings.ShaderStages = + { + { + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .stage = VK_SHADER_STAGE_VERTEX_BIT, + .module = *ShaderVertex, + .pName = "main", + .pSpecializationInfo = nullptr + }, { + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .stage = VK_SHADER_STAGE_FRAGMENT_BIT, + .module = *ShaderFragment, + .pName = "main", + .pSpecializationInfo = nullptr + } + }; + + Pipeline::init(instance); +} + +PipelineVGF::PipelineVGF(std::shared_ptr layout, const std::string &vertex, + const std::string &geometry, const std::string &fragment) + : Pipeline(layout), PathVertex(vertex), PathGeometry(geometry), PathFragment(fragment) +{ + // Settings.ShaderVertexBindings = + // { + // { + // .binding = 0, + // .stride = sizeof(Client::Chunk::Vertex), + // .inputRate = VK_VERTEX_INPUT_RATE_VERTEX + // } + // }; + + // Settings.ShaderVertexAttribute = + // { + // { + // .location = 0, + // .binding = 0, + // .format = VK_FORMAT_R32_UINT, + // .offset = 0 + // } + // }; +} + +PipelineVGF::~PipelineVGF() = default; + +void PipelineVGF::init(Vulkan *instance) +{ + if(!ShaderVertex) + ShaderVertex = instance->createShaderFromFile(PathVertex); + + if(!ShaderGeometry) + ShaderGeometry = instance->createShaderFromFile(PathGeometry); + + if(!ShaderFragment) + ShaderFragment = instance->createShaderFromFile(PathFragment); + + Settings.ShaderStages = + { + { + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .stage = VK_SHADER_STAGE_VERTEX_BIT, + .module = *ShaderVertex, + .pName = "main", + .pSpecializationInfo = nullptr + }, { + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .stage = VK_SHADER_STAGE_GEOMETRY_BIT, + .module = *ShaderGeometry, + .pName = "main", + .pSpecializationInfo = nullptr + }, { + .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, + .pNext = nullptr, + .flags = 0, + .stage = VK_SHADER_STAGE_FRAGMENT_BIT, + .module = *ShaderFragment, + .pName = "main", + .pSpecializationInfo = nullptr + } + }; + + Pipeline::init(instance); +} + } diff --git a/Src/Client/Vulkan/Vulkan.hpp b/Src/Client/Vulkan/Vulkan.hpp index 140f1e2..cf640b2 100644 --- a/Src/Client/Vulkan/Vulkan.hpp +++ b/Src/Client/Vulkan/Vulkan.hpp @@ -234,11 +234,11 @@ public: std::queue> BeforeDraw; DrawState State = DrawState::Begin; } Screen; - + struct { DestroyLock UseLock; std::thread MainThread; - std::unique_ptr RSession; + std::shared_ptr RSession; std::unique_ptr Session; std::list ImGuiInterfaces; @@ -358,17 +358,10 @@ public: void gui_ConnectedToServer(); }; -enum class EnumRebuildType { - None, - Need, - Urgently -}; - class IVulkanDependent : public std::enable_shared_from_this { protected: virtual void free(Vulkan *instance) = 0; virtual void init(Vulkan *instance) = 0; - virtual EnumRebuildType needRebuild(); friend Vulkan; friend Pipeline; @@ -449,8 +442,6 @@ protected: virtual void free(Vulkan *instance) override; virtual void init(Vulkan *instance) override; - // Если необходимо изменить графический конвейер, будет вызвано free и init - virtual EnumRebuildType needRebuild() override; public: Pipeline(std::shared_ptr layout); @@ -1000,5 +991,29 @@ public: size_t getLastUpdate() { return LastUpdate; } }; +class PipelineVF : public Pipeline { + std::string PathVertex, PathFragment; + std::shared_ptr ShaderVertex, ShaderFragment; + +protected: + virtual void init(Vulkan *instance) override; + +public: + PipelineVF(std::shared_ptr layout, const std::string &vertex, const std::string &fragment); + virtual ~PipelineVF(); +}; + +class PipelineVGF : public Pipeline { + std::string PathVertex, PathGeometry, PathFragment; + std::shared_ptr ShaderVertex, ShaderGeometry, ShaderFragment; + +protected: + virtual void init(Vulkan *instance) override; + +public: + PipelineVGF(std::shared_ptr layout, const std::string &vertex, const std::string &geometry, const std::string &fragment); + virtual ~PipelineVGF(); +}; + } /* namespace TOS::Navie::VK */ diff --git a/Src/Client/Vulkan/VulkanRenderSession.cpp b/Src/Client/Vulkan/VulkanRenderSession.cpp index db7bc08..f85349f 100644 --- a/Src/Client/Vulkan/VulkanRenderSession.cpp +++ b/Src/Client/Vulkan/VulkanRenderSession.cpp @@ -1,19 +1,464 @@ #include "VulkanRenderSession.hpp" +#include namespace LV::Client::VK { -VulkanRenderSession::VulkanRenderSession(VK::Vulkan *vkInst) - : VkInst(vkInst), - MainAtlas(vkInst) +VulkanRenderSession::VulkanRenderSession() { - assert(VkInst); } VulkanRenderSession::~VulkanRenderSession() { } +void VulkanRenderSession::free(Vulkan *instance) { + if(instance && instance->Graphics.Device) + { + if(VoxelOpaquePipeline) + vkDestroyPipeline(instance->Graphics.Device, VoxelOpaquePipeline, nullptr); + if(VoxelTransparentPipeline) + vkDestroyPipeline(instance->Graphics.Device, VoxelTransparentPipeline, nullptr); + if(NodeStaticOpaquePipeline) + vkDestroyPipeline(instance->Graphics.Device, NodeStaticOpaquePipeline, nullptr); + if(NodeStaticTransparentPipeline) + vkDestroyPipeline(instance->Graphics.Device, NodeStaticTransparentPipeline, nullptr); + + if(MainAtlas_LightMap_PipelineLayout) + vkDestroyPipelineLayout(instance->Graphics.Device, MainAtlas_LightMap_PipelineLayout, nullptr); + + if(MainAtlasDescLayout) + vkDestroyDescriptorSetLayout(instance->Graphics.Device, MainAtlasDescLayout, nullptr); + if(LightMapDescLayout) + vkDestroyDescriptorSetLayout(instance->Graphics.Device, LightMapDescLayout, nullptr); + } + + VoxelOpaquePipeline = VK_NULL_HANDLE; + VoxelTransparentPipeline = VK_NULL_HANDLE; + NodeStaticOpaquePipeline = VK_NULL_HANDLE; + NodeStaticTransparentPipeline = VK_NULL_HANDLE; + + MainAtlas_LightMap_PipelineLayout = VK_NULL_HANDLE; + + MainAtlasDescLayout = VK_NULL_HANDLE; + LightMapDescLayout = VK_NULL_HANDLE; +} + +void VulkanRenderSession::init(Vulkan *instance) { + if(VkInst != instance) { + VkInst = instance; + } + + // Разметка дескрипторов + if(!MainAtlasDescLayout) { + 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_VERTEX_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() + }; + + assert(!vkCreateDescriptorSetLayout( + instance->Graphics.Device, &descriptorLayout, nullptr, &MainAtlasDescLayout)); + } + + if(!LightMapDescLayout) { + 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_VERTEX_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() + }; + + assert(!vkCreateDescriptorSetLayout( instance->Graphics.Device, &descriptorLayout, nullptr, &LightMapDescLayout)); + } + + + std::vector worldWideShaderPushConstants = + { + { + .stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_GEOMETRY_BIT, + .offset = 0, + .size = uint32_t(sizeof(WorldPCO)) + } + }; + + // Разметка графических конвейеров + if(!MainAtlas_LightMap_PipelineLayout) { + std::vector layouts = + { + MainAtlasDescLayout, + LightMapDescLayout + }; + + 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() + }; + + assert(!vkCreatePipelineLayout(instance->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; + memset(&infoPipelineCache, 0, sizeof(infoPipelineCache)); + infoPipelineCache.sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO; + + // Конвейеры для вокселей + if(!VoxelOpaquePipeline || !VoxelTransparentPipeline + || !NodeStaticOpaquePipeline || !NodeStaticTransparentPipeline) + { + // Для статичных непрозрачных и полупрозрачных вокселей + if(!VoxelShaderVertex) + VoxelShaderVertex = VkInst->createShaderFromFile("assets/shaders/chunk/voxel.vert.bin"); + + if(!VoxelShaderGeometry) + VoxelShaderGeometry = VkInst->createShaderFromFile("assets/shaders/chunk/voxel.geom.bin"); + + if(!VoxelShaderFragmentOpaque) + VoxelShaderFragmentOpaque = VkInst->createShaderFromFile("assets/shaders/chunk/voxel_opaque.frag.bin"); + + if(!VoxelShaderFragmentTransparent) + VoxelShaderFragmentTransparent = VkInst->createShaderFromFile("assets/shaders/chunk/voxel_transparent.frag.bin"); + + // Конвейер шейдеров + 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_OR_EQUAL, + .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 = instance->Graphics.RenderPass, + .subpass = 0, + .basePipelineHandle = nullptr, + .basePipelineIndex = 0 + }; + + if(!VoxelOpaquePipeline) + assert(!vkCreateGraphicsPipelines(instance->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 + }; + + assert(!vkCreateGraphicsPipelines(instance->Graphics.Device, VK_NULL_HANDLE, 1, &pipeline, nullptr, &VoxelTransparentPipeline)); + } + + // Для статичных непрозрачных и полупрозрачных нод + if(!NodeShaderVertex) + NodeShaderVertex = VkInst->createShaderFromFile("assets/shaders/chunk/node.vert.bin"); + + if(!NodeShaderGeometry) + NodeShaderGeometry = VkInst->createShaderFromFile("assets/shaders/chunk/node.geom.bin"); + + if(!NodeShaderFragmentOpaque) + NodeShaderFragmentOpaque = VkInst->createShaderFromFile("assets/shaders/chunk/node_opaque.frag.bin"); + + if(!NodeShaderFragmentTransparent) + NodeShaderFragmentTransparent = VkInst->createShaderFromFile("assets/shaders/chunk/node_transparent.frag.bin"); + + 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) { + assert(!vkCreateGraphicsPipelines(instance->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 + }; + + assert(!vkCreateGraphicsPipelines(instance->Graphics.Device, VK_NULL_HANDLE, + 1, &pipeline, nullptr, &NodeStaticTransparentPipeline)); + } + } +} + void VulkanRenderSession::onDefTexture(TextureId_c id, std::vector &&info) { } @@ -60,4 +505,12 @@ void VulkanRenderSession::setCameraPos(WorldId_c worldId, Pos::Object pos, glm:: Quat = quat; } +void VulkanRenderSession::beforeDraw() { + +} + +void VulkanRenderSession::drawWorld(GlobalTime gTime, float dTime, VkCommandBuffer drawCmd) { + +} + } \ No newline at end of file diff --git a/Src/Client/Vulkan/VulkanRenderSession.hpp b/Src/Client/Vulkan/VulkanRenderSession.hpp index a1667a6..89df9ed 100644 --- a/Src/Client/Vulkan/VulkanRenderSession.hpp +++ b/Src/Client/Vulkan/VulkanRenderSession.hpp @@ -1,23 +1,124 @@ #pragma once #include "Client/Abstract.hpp" +#include "Common/Abstract.hpp" #include +#include +/* + У движка есть один текстурный атлас 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 { -class VulkanRenderSession : public IRenderSession { - VK::Vulkan *VkInst; +struct WorldPCO { + glm::mat4 ProjView, Model; +}; + +static_assert(sizeof(WorldPCO) == 128); + +/* + Воксели рендерятся точками, которые распаковываются в квадратные плоскости + + В чанке по оси 256 вокселей, и 257 позиций вершин (включая дальнюю границу чанка) + 9 бит на позицию *3 оси = 27 бит + Указание материала 16 бит +*/ + +struct VoxelVertexPoint { + uint32_t + FX : 9, FY : 9, FZ : 9, // Позиция + Place : 3, // Положение распространения xz, xy, zy, и обратные + N1 : 1, // Не занято + LS : 1, // Масштаб карты освещения (1м/16 или 1м) + TX : 8, TY : 8, // Размер+1 + VoxMtl : 16, // Материал вокселя DefVoxelId_t + LU : 14, LV : 14, // Позиция на карте освещения + N2 : 2; // Не занято +}; + +/* + Из-за карт освещения индексов не будет + Максимальный размер меша 14^3 м от центра ноды + Координатное пространство то же, что и у вокселей + 8 позиций с двух сторон + Рисуется полигонами +*/ + +struct NodeVertexStatic { + uint32_t + FX : 9, FY : 9, FZ : 9, // Позиция -112 ~ 369 / 16 + N1 : 4, // Не занято + LS : 1, // Масштаб карты освещения (1м/16 или 1м) + Tex : 18, // Текстура + N2 : 14, // Не занято + TU : 16, TV : 16; // UV на текстуре +}; + +class VulkanRenderSession : public IRenderSession, public IVulkanDependent { + VK::Vulkan *VkInst = nullptr; + // Доступ к миру на стороне клиента IServerSession *ServerSession = nullptr; + // Положение камеры WorldId_c WorldId; Pos::Object Pos; glm::quat Quat; - VK::AtlasImage MainAtlas; + /* + .binding = 0, + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, Текстурный атлас + .binding = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, Данные к атласу + */ + VkDescriptorSetLayout MainAtlasDescLayout = VK_NULL_HANDLE; + /* + .binding = 2, + .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, Карта освещения + .binding = 3, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, Информация о размерах карты для приведения размеров + */ + VkDescriptorSetLayout LightMapDescLayout = 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; + virtual void free(Vulkan *instance) override; + virtual void init(Vulkan *instance) override; + public: - VulkanRenderSession(VK::Vulkan *vkInst); + WorldPCO PCO; + +public: + VulkanRenderSession(); virtual ~VulkanRenderSession(); void setServerSession(IServerSession *serverSession) { @@ -38,6 +139,13 @@ public: virtual void onChunksChange(WorldId_c worldId, const std::vector &changeOrAddList, const std::vector &remove) override; virtual void setCameraPos(WorldId_c 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); }; } \ No newline at end of file diff --git a/Work/assets/shaders/chunk/node.geom b/Work/assets/shaders/chunk/node.geom new file mode 100644 index 0000000..5a91ac2 --- /dev/null +++ b/Work/assets/shaders/chunk/node.geom @@ -0,0 +1,28 @@ +#version 450 + +layout (triangles) in; +layout (triangle_strip, max_vertices = 3) out; + +layout(location = 0) in GeometryObj { + vec3 GeoPos; // Реальная позиция в мире + uint Texture; // Текстура + vec2 UV; +} Geometry[]; + +layout(location = 0) out FragmentObj { + vec3 GeoPos; // Реальная позиция в мире + uint Texture; // Текстура + vec2 UV; +} Fragment; + +void main() { + for(int iter = 0; iter < 3; iter++) { + gl_Position = gl_in[iter].gl_Position; + Fragment.GeoPos = Geometry[iter].GeoPos; + Fragment.Texture = Geometry[iter].Texture; + Fragment.UV = Geometry[iter].UV; + EmitVertex(); + } + + EndPrimitive(); +} diff --git a/Work/assets/shaders/chunk/node.geom.bin b/Work/assets/shaders/chunk/node.geom.bin new file mode 100644 index 0000000..daf5c8d Binary files /dev/null and b/Work/assets/shaders/chunk/node.geom.bin differ diff --git a/Work/assets/shaders/chunk/node.vert b/Work/assets/shaders/chunk/node.vert new file mode 100644 index 0000000..ed638f3 --- /dev/null +++ b/Work/assets/shaders/chunk/node.vert @@ -0,0 +1,44 @@ +#version 450 + +layout(location = 0) in uvec4 Vertex; + +layout(location = 0) out GeometryObj { + vec3 GeoPos; // Реальная позиция в мире + uint Texture; // Текстура + vec2 UV; +} Geometry; + +layout(push_constant) uniform UniformBufferObject { + mat4 projview; + mat4 model; +} ubo; + + +// struct NodeVertexStatic { +// uint32_t +// FX : 9, FY : 9, FZ : 9, // Позиция -112 ~ 369 / 16 +// N1 : 4, // Не занято +// LS : 1, // Масштаб карты освещения (1м/16 или 1м) +// Tex : 18, // Текстура +// N2 : 14, // Не занято +// TU : 16, TV : 16; // UV на текстуре +// }; + +void main() +{ + vec4 baseVec = ubo.model*vec4( + float(Vertex.x & 0x1ff) / 16.f - 7, + float((Vertex.x >> 9) & 0x1ff) / 16.f - 7, + float((Vertex.x >> 18) & 0x1ff) / 16.f - 7, + 1 + ); + + Geometry.GeoPos = baseVec.xyz; + Geometry.Texture = Vertex.y & 0x3ffff; + Geometry.UV = vec2( + float(Vertex.z & 0xffff) / pow(2, 16), + float((Vertex.z >> 16) & 0xffff) / pow(2, 16) + ); + + gl_Position = ubo.projview*baseVec; +} diff --git a/Work/assets/shaders/chunk/node.vert.bin b/Work/assets/shaders/chunk/node.vert.bin new file mode 100644 index 0000000..614f723 Binary files /dev/null and b/Work/assets/shaders/chunk/node.vert.bin differ diff --git a/Work/assets/shaders/chunk/node_opaque.frag b/Work/assets/shaders/chunk/node_opaque.frag new file mode 100644 index 0000000..174bcc7 --- /dev/null +++ b/Work/assets/shaders/chunk/node_opaque.frag @@ -0,0 +1,23 @@ +#version 450 + +layout(location = 0) in FragmentObj { + vec3 GeoPos; // Реальная позиция в мире + uint Texture; // Текстура + vec2 UV; +} Fragment; + +layout(location = 0) out vec4 Frame; + +uniform layout(set = 0, binding = 0) sampler2DArray MainAtlas; +layout(set = 0, binding = 1) readonly buffer MainAtlasLayoutObj { + vec3 Color; +} MainAtlasLayout; + +uniform layout(set = 1, binding = 0) sampler2DArray LightMap; +layout(set = 1, binding = 1) readonly buffer LightMapLayoutObj { + vec3 Color; +} LightMapLayout; + +void main() { + Frame = vec4(1); +} \ No newline at end of file diff --git a/Work/assets/shaders/chunk/node_opaque.frag.bin b/Work/assets/shaders/chunk/node_opaque.frag.bin new file mode 100644 index 0000000..6bc2034 Binary files /dev/null and b/Work/assets/shaders/chunk/node_opaque.frag.bin differ diff --git a/Work/assets/shaders/chunk/node_transparent.frag b/Work/assets/shaders/chunk/node_transparent.frag new file mode 100644 index 0000000..174bcc7 --- /dev/null +++ b/Work/assets/shaders/chunk/node_transparent.frag @@ -0,0 +1,23 @@ +#version 450 + +layout(location = 0) in FragmentObj { + vec3 GeoPos; // Реальная позиция в мире + uint Texture; // Текстура + vec2 UV; +} Fragment; + +layout(location = 0) out vec4 Frame; + +uniform layout(set = 0, binding = 0) sampler2DArray MainAtlas; +layout(set = 0, binding = 1) readonly buffer MainAtlasLayoutObj { + vec3 Color; +} MainAtlasLayout; + +uniform layout(set = 1, binding = 0) sampler2DArray LightMap; +layout(set = 1, binding = 1) readonly buffer LightMapLayoutObj { + vec3 Color; +} LightMapLayout; + +void main() { + Frame = vec4(1); +} \ No newline at end of file diff --git a/Work/assets/shaders/chunk/node_transparent.frag.bin b/Work/assets/shaders/chunk/node_transparent.frag.bin new file mode 100644 index 0000000..6bc2034 Binary files /dev/null and b/Work/assets/shaders/chunk/node_transparent.frag.bin differ diff --git a/Work/assets/shaders/chunk/voxel.geom b/Work/assets/shaders/chunk/voxel.geom new file mode 100644 index 0000000..1afc36e --- /dev/null +++ b/Work/assets/shaders/chunk/voxel.geom @@ -0,0 +1,244 @@ +#version 450 + +layout (points) in; +layout (triangle_strip, max_vertices = 4) out; + +layout(location = 0) in highp uvec3 Geometry[]; + +layout(location = 0) out Fragment { + vec3 GeoPos; // Реальная позиция в мире + uint VoxMTL; // Материал вокселя + vec2 LUV; +} fragment; + +layout(push_constant) uniform UniformBufferObject { + mat4 projview; + mat4 model; +} ubo; + +// struct VoxelVertexPoint { +// uint32_t +// FX : 9, FY : 9, FZ : 9, // Позиция +// Place : 3, // Положение распространения xz, xy, zy, и обратные +// N1 : 1, // Не занято +// LS : 1, // Масштаб карты освещения (1м/16 или 1м) +// TX : 8, TY : 8, // Размер+1 +// VoxMtl : 16, // Материал вокселя DefVoxelId_t +// LU : 14, LV : 14, // Позиция на карте освещения +// N2 : 2; // Не занято +// }; + +void main() { + vec4 baseVec = vec4( + float(Geometry[0].x & 0x1ff) / 16.f, + float((Geometry[0].x >> 9) & 0x1ff) / 16.f, + float((Geometry[0].x >> 18) & 0x1ff) / 16.f, + 1 + ); + + vec2 size = vec2( + float(Geometry[0].y & 0xff)+1, + float((Geometry[0].y >> 8) & 0xff)+1 + ); + + uint voxMTL = ((Geometry[0].y >> 16) & 0xffff); + vec2 luv = vec2(float(Geometry[0].z & 0x3fff)+0.5f, float((Geometry[0].z >> 14) & 0x3fff)+0.5f); + + // Стартовая вершина + vec4 tempVec = baseVec; + tempVec = ubo.model*tempVec; + fragment.GeoPos = vec3(tempVec); + fragment.VoxMTL = voxMTL; + fragment.LUV = luv; + gl_Position = ubo.projview*tempVec; + EmitVertex(); + + + int place = int(Geometry[0].x >> 27) & 0x3; + switch(place) { + case 0: // xz + tempVec = baseVec; + tempVec.x += size.x; + tempVec = ubo.model*tempVec; + fragment.GeoPos = vec3(tempVec); + fragment.VoxMTL = voxMTL; + fragment.LUV = luv+vec2(size.x, 0); + gl_Position = ubo.projview*tempVec; + EmitVertex(); + + tempVec = baseVec; + tempVec.z += size.y; + tempVec = ubo.model*tempVec; + fragment.GeoPos = vec3(tempVec); + fragment.VoxMTL = voxMTL; + fragment.LUV = luv+vec2(0, size.y); + gl_Position = ubo.projview*tempVec; + EmitVertex(); + + tempVec = baseVec; + tempVec.x += size.x; + tempVec.z += size.y; + tempVec = ubo.model*tempVec; + fragment.GeoPos = vec3(tempVec); + fragment.VoxMTL = voxMTL; + fragment.LUV = luv+vec2(size.x, size.y); + gl_Position = ubo.projview*tempVec; + EmitVertex(); + + break; + case 1: // xy + tempVec = baseVec; + tempVec.x += size.x; + tempVec = ubo.model*tempVec; + fragment.GeoPos = vec3(tempVec); + fragment.VoxMTL = voxMTL; + fragment.LUV = luv+vec2(size.x, 0); + gl_Position = ubo.projview*tempVec; + EmitVertex(); + + tempVec = baseVec; + tempVec.y += size.y; + tempVec = ubo.model*tempVec; + fragment.GeoPos = vec3(tempVec); + fragment.VoxMTL = voxMTL; + fragment.LUV = luv+vec2(0, size.y); + gl_Position = ubo.projview*tempVec; + EmitVertex(); + + tempVec = baseVec; + tempVec.x += size.x; + tempVec.y += size.y; + tempVec = ubo.model*tempVec; + fragment.GeoPos = vec3(tempVec); + fragment.VoxMTL = voxMTL; + fragment.LUV = luv+vec2(size.x, size.y); + gl_Position = ubo.projview*tempVec; + EmitVertex(); + + break; + case 2: // zy + tempVec = baseVec; + tempVec.z += size.x; + tempVec = ubo.model*tempVec; + fragment.GeoPos = vec3(tempVec); + fragment.VoxMTL = voxMTL; + fragment.LUV = luv+vec2(size.x, 0); + gl_Position = ubo.projview*tempVec; + EmitVertex(); + + tempVec = baseVec; + tempVec.y += size.y; + tempVec = ubo.model*tempVec; + fragment.GeoPos = vec3(tempVec); + fragment.VoxMTL = voxMTL; + fragment.LUV = luv+vec2(0, size.y); + gl_Position = ubo.projview*tempVec; + EmitVertex(); + + tempVec = baseVec; + tempVec.z += size.x; + tempVec.y += size.y; + tempVec = ubo.model*tempVec; + fragment.GeoPos = vec3(tempVec); + fragment.VoxMTL = voxMTL; + fragment.LUV = luv+vec2(size.x, size.y); + gl_Position = ubo.projview*tempVec; + EmitVertex(); + + break; + case 3: // xz inv + tempVec = baseVec; + tempVec.z += size.y; + tempVec = ubo.model*tempVec; + fragment.GeoPos = vec3(tempVec); + fragment.VoxMTL = voxMTL; + fragment.LUV = luv+vec2(size.x, 0); + gl_Position = ubo.projview*tempVec; + EmitVertex(); + + tempVec = baseVec; + tempVec.x += size.x; + tempVec = ubo.model*tempVec; + fragment.GeoPos = vec3(tempVec); + fragment.VoxMTL = voxMTL; + fragment.LUV = luv+vec2(0, size.y); + gl_Position = ubo.projview*tempVec; + EmitVertex(); + + tempVec = baseVec; + tempVec.x += size.x; + tempVec.z += size.y; + tempVec = ubo.model*tempVec; + fragment.GeoPos = vec3(tempVec); + fragment.VoxMTL = voxMTL; + fragment.LUV = luv+vec2(size.x, size.y); + gl_Position = ubo.projview*tempVec; + EmitVertex(); + + break; + case 4: // xy inv + tempVec = baseVec; + tempVec.y += size.y; + tempVec = ubo.model*tempVec; + fragment.GeoPos = vec3(tempVec); + fragment.VoxMTL = voxMTL; + fragment.LUV = luv+vec2(size.x, 0); + gl_Position = ubo.projview*tempVec; + EmitVertex(); + + tempVec = baseVec; + tempVec.x += size.x; + tempVec = ubo.model*tempVec; + fragment.GeoPos = vec3(tempVec); + fragment.VoxMTL = voxMTL; + fragment.LUV = luv+vec2(0, size.y); + gl_Position = ubo.projview*tempVec; + EmitVertex(); + + tempVec = baseVec; + tempVec.x += size.x; + tempVec.y += size.y; + tempVec = ubo.model*tempVec; + fragment.GeoPos = vec3(tempVec); + fragment.VoxMTL = voxMTL; + fragment.LUV = luv+vec2(size.x, size.y); + gl_Position = ubo.projview*tempVec; + EmitVertex(); + + break; + case 5: // zy inv + tempVec = baseVec; + tempVec.y += size.y; + tempVec = ubo.model*tempVec; + fragment.GeoPos = vec3(tempVec); + fragment.VoxMTL = voxMTL; + fragment.LUV = luv+vec2(size.x, 0); + gl_Position = ubo.projview*tempVec; + EmitVertex(); + + tempVec = baseVec; + tempVec.z += size.x; + tempVec = ubo.model*tempVec; + fragment.GeoPos = vec3(tempVec); + fragment.VoxMTL = voxMTL; + fragment.LUV = luv+vec2(0, size.y); + gl_Position = ubo.projview*tempVec; + EmitVertex(); + + tempVec = baseVec; + tempVec.z += size.x; + tempVec.y += size.y; + tempVec = ubo.model*tempVec; + fragment.GeoPos = vec3(tempVec); + fragment.VoxMTL = voxMTL; + fragment.LUV = luv+vec2(size.x, size.y); + gl_Position = ubo.projview*tempVec; + EmitVertex(); + + break; + default: + break; + } + + EndPrimitive(); +} diff --git a/Work/assets/shaders/chunk/voxel.geom.bin b/Work/assets/shaders/chunk/voxel.geom.bin new file mode 100644 index 0000000..1aa3a6e Binary files /dev/null and b/Work/assets/shaders/chunk/voxel.geom.bin differ diff --git a/Work/assets/shaders/chunk/voxel.vert b/Work/assets/shaders/chunk/voxel.vert new file mode 100644 index 0000000..c625e96 --- /dev/null +++ b/Work/assets/shaders/chunk/voxel.vert @@ -0,0 +1,10 @@ +#version 450 + +layout(location = 0) in uvec3 Vertex; +layout(location = 0) out uvec3 Geometry; + + +void main() +{ + Geometry = Vertex; +} diff --git a/Work/assets/shaders/chunk/voxel.vert.bin b/Work/assets/shaders/chunk/voxel.vert.bin new file mode 100644 index 0000000..09310c9 Binary files /dev/null and b/Work/assets/shaders/chunk/voxel.vert.bin differ diff --git a/Work/assets/shaders/chunk/voxel_opaque.frag b/Work/assets/shaders/chunk/voxel_opaque.frag new file mode 100644 index 0000000..b1a6436 --- /dev/null +++ b/Work/assets/shaders/chunk/voxel_opaque.frag @@ -0,0 +1,23 @@ +#version 450 + +layout(location = 0) in Fragment { + vec3 GeoPos; // Реальная позиция в мире + uint VoxMTL; // Материал вокселя + vec2 LUV; +} fragment; + +layout(location = 0) out vec4 Frame; + +uniform layout(set = 0, binding = 0) sampler2DArray MainAtlas; +layout(set = 0, binding = 1) readonly buffer MainAtlasLayoutObj { + vec3 Color; +} MainAtlasLayout; + +uniform layout(set = 1, binding = 0) sampler2DArray LightMap; +layout(set = 1, binding = 1) readonly buffer LightMapLayoutObj { + vec3 Color; +} LightMapLayout; + +void main() { + Frame = vec4(1); +} diff --git a/Work/assets/shaders/chunk/voxel_opaque.frag.bin b/Work/assets/shaders/chunk/voxel_opaque.frag.bin new file mode 100644 index 0000000..61a69b1 Binary files /dev/null and b/Work/assets/shaders/chunk/voxel_opaque.frag.bin differ diff --git a/Work/assets/shaders/chunk/voxel_transparent.frag b/Work/assets/shaders/chunk/voxel_transparent.frag new file mode 100644 index 0000000..b1a6436 --- /dev/null +++ b/Work/assets/shaders/chunk/voxel_transparent.frag @@ -0,0 +1,23 @@ +#version 450 + +layout(location = 0) in Fragment { + vec3 GeoPos; // Реальная позиция в мире + uint VoxMTL; // Материал вокселя + vec2 LUV; +} fragment; + +layout(location = 0) out vec4 Frame; + +uniform layout(set = 0, binding = 0) sampler2DArray MainAtlas; +layout(set = 0, binding = 1) readonly buffer MainAtlasLayoutObj { + vec3 Color; +} MainAtlasLayout; + +uniform layout(set = 1, binding = 0) sampler2DArray LightMap; +layout(set = 1, binding = 1) readonly buffer LightMapLayoutObj { + vec3 Color; +} LightMapLayout; + +void main() { + Frame = vec4(1); +} diff --git a/Work/assets/shaders/chunk/voxel_transparent.frag.bin b/Work/assets/shaders/chunk/voxel_transparent.frag.bin new file mode 100644 index 0000000..61a69b1 Binary files /dev/null and b/Work/assets/shaders/chunk/voxel_transparent.frag.bin differ diff --git a/Work/assets/shaders/compile.sh b/Work/assets/shaders/compile.sh new file mode 100755 index 0000000..79b3b01 --- /dev/null +++ b/Work/assets/shaders/compile.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +function compile_shaders() { + local source_dir="$1" + + for item in "$source_dir"/*; do + filename=$(basename "$item") + if [ -d "$item" ]; then + compile_shaders "$source_dir"/"$filename" + + elif [ -f "$item" ] && [ $item -nt $item.bin ] && ([[ $filename = *'.frag' ]] || [[ $filename = *'.vert' ]] || [[ $filename = *'.geom' ]]); then + echo $filename + glslc $item -o $item.bin --target-env=vulkan1.2 + fi + done +} + +compile_shaders .