From 8c13938b068fc1c63643af4c149a02565dd36e45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=95=D0=B2=D0=B3=D0=B5=D0=BD=D0=B8=D0=B9?= Date: Thu, 13 Feb 2025 22:22:10 +0600 Subject: [PATCH] =?UTF-8?q?=D0=93=D1=80=D0=B0=D1=84=D0=B8=D1=87=D0=B5?= =?UTF-8?q?=D1=81=D0=BA=D0=B8=D0=B5=20=D0=BA=D0=BE=D0=BD=D0=B2=D0=B5=D0=B9?= =?UTF-8?q?=D0=B5=D1=80=D1=8B=20=D0=B4=D0=BB=D1=8F=20=D0=B2=D0=BE=D0=BA?= =?UTF-8?q?=D1=81=D0=B5=D0=BB=D0=B5=D0=B9=20=D0=B8=20=D0=BD=D0=BE=D0=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Src/Client/Abstract.hpp | 22 +- Src/Client/ServerSession.cpp | 29 +- Src/Client/ServerSession.hpp | 10 +- Src/Client/Vulkan/Vulkan.cpp | 193 ++++++-- Src/Client/Vulkan/Vulkan.hpp | 37 +- Src/Client/Vulkan/VulkanRenderSession.cpp | 461 +++++++++++++++++- Src/Client/Vulkan/VulkanRenderSession.hpp | 116 ++++- Work/assets/shaders/chunk/node.geom | 28 ++ Work/assets/shaders/chunk/node.geom.bin | Bin 0 -> 2232 bytes Work/assets/shaders/chunk/node.vert | 44 ++ Work/assets/shaders/chunk/node.vert.bin | Bin 0 -> 2584 bytes Work/assets/shaders/chunk/node_opaque.frag | 23 + .../assets/shaders/chunk/node_opaque.frag.bin | Bin 0 -> 1264 bytes .../shaders/chunk/node_transparent.frag | 23 + .../shaders/chunk/node_transparent.frag.bin | Bin 0 -> 1264 bytes Work/assets/shaders/chunk/voxel.geom | 244 +++++++++ Work/assets/shaders/chunk/voxel.geom.bin | Bin 0 -> 16300 bytes Work/assets/shaders/chunk/voxel.vert | 10 + Work/assets/shaders/chunk/voxel.vert.bin | Bin 0 -> 440 bytes Work/assets/shaders/chunk/voxel_opaque.frag | 23 + .../shaders/chunk/voxel_opaque.frag.bin | Bin 0 -> 1264 bytes .../shaders/chunk/voxel_transparent.frag | 23 + .../shaders/chunk/voxel_transparent.frag.bin | Bin 0 -> 1264 bytes Work/assets/shaders/compile.sh | 18 + 24 files changed, 1239 insertions(+), 65 deletions(-) create mode 100644 Work/assets/shaders/chunk/node.geom create mode 100644 Work/assets/shaders/chunk/node.geom.bin create mode 100644 Work/assets/shaders/chunk/node.vert create mode 100644 Work/assets/shaders/chunk/node.vert.bin create mode 100644 Work/assets/shaders/chunk/node_opaque.frag create mode 100644 Work/assets/shaders/chunk/node_opaque.frag.bin create mode 100644 Work/assets/shaders/chunk/node_transparent.frag create mode 100644 Work/assets/shaders/chunk/node_transparent.frag.bin create mode 100644 Work/assets/shaders/chunk/voxel.geom create mode 100644 Work/assets/shaders/chunk/voxel.geom.bin create mode 100644 Work/assets/shaders/chunk/voxel.vert create mode 100644 Work/assets/shaders/chunk/voxel.vert.bin create mode 100644 Work/assets/shaders/chunk/voxel_opaque.frag create mode 100644 Work/assets/shaders/chunk/voxel_opaque.frag.bin create mode 100644 Work/assets/shaders/chunk/voxel_transparent.frag create mode 100644 Work/assets/shaders/chunk/voxel_transparent.frag.bin create mode 100755 Work/assets/shaders/compile.sh 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 0000000000000000000000000000000000000000..daf5c8dbf7b572f03bacaf6f272d8f15a7b201f6 GIT binary patch literal 2232 zcmZ{k*-jKu5QYyh3yW;BxgZYWj^YN0BFG3hUXWmday!AHL)tVA>1hmj5lfPvMaW^9@p#EUAhKcyM9hs$+bz>Hnuiqn(^+; z?A${#`dkMpiS@g##4E_(-|<86OOmUSyOJ5nfHH`WCUzmQWhVPAb|n{MKNULkqmOTD zqTssKN4>VWS=(5zHx3W$&G;nn>VY46^<6*m8nOS$Bd;@;=ZB5pXg8lwR7Sp6{7%*I zV=t0&MVqXmM6nn?ur3}X=cF^=={~#1vPV%{@1oy*u%m1uS*ptRMgJP?J zf4Js1EZo0=PM@f#JJcG zHZ3ileJ_qqj311Z=Lc)$2g~#G28~OmtLc=?n4CANh0W?o)hPxq7~1mK1I9fWh7HEM zg4^zxI5sQK4c5wSd-B|1t=!x@J=9c<|JQ_sMcrv>i*aU)4G9IGR1SRDBidh*u1M&m zBEKw+fKO-fF4-*e2V}!BES5p(QF%ks70HA2Qh@+E=p6E_+sYUcY=M~RqH;hH=gaK9^V;q!QkbN=mi{Je7;>^#3wTwZv5-g z>G{+1-jq-OV4VL}=5xmrnm_X*hjSBy#HfkSEWy~r9P@kV`<^gx>-)a!HVHT! z_{_OmZ1VBvbq4Zy z11#`gLW#bXIEc$v|xgtuV4_~aPxylmrTW(yK{!I>v{%> 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 0000000000000000000000000000000000000000..614f723a38f6aa3a4f5a66cd31da066a2f749ea5 GIT binary patch literal 2584 zcmZ9M*>YP&5QfK#?BL)mge*WX3T6+O0EtNmNo;440f8JGn;pfLkExTAC6$(taOEm) zxaE~{!)x#u+@#F+opUCo(zt$g_uoA|Jw1IqF+M*XN@L-0m<^4Puj64njG-P5Q<(vmux1DPzux3 zAHqf}-EDXGRtCGfNw>EBfc)HQD)ZqEoo@TV!!-F_eTrS~@m_oFour8@QM>mUY`4)T zvwLkO-G1^&UnzWwuadO)l79Cw>9ZViF8H-}FV8Dy-h0WT{-B!>H<|6VFB_YAF8dX8 zp^|+n{D}OmSzk-K@l^BWyfOOPy|kaUTd>nvyys+m+Ul>Tf6z_$j~zw`m9yI`Qv*>&OLJWB68|-&f5vH8gq^adG%9h zQ^+znO_cZ0Hona7A*7r*gFRE|`sc9CEhmhB9_cL0UoC8BQ0^r55o8&Rf%AoZw6NW8 z?~OH|@h6aF;Ih6Kk#e!Vlj!D>_dIRC-DaKbu>@Tqo4syTu`5J_< zUy1eH#q#Z}=R2hNz0XA1Nnc5(LP&n&P|6H z@AhiJ|3!@UYX#>l{aJA53ciet=e>?Tdfq&o{FCCmH__#syI8|p=<>rgyp1g%Yj_7; zZn%bbvE|Dk*6<$sXbtD##P{eDA#bANpof$wC@bza(cmGgb{Uj=eCUfplt^MaFijR9xH`}(b12UmdKkomOT z6Z^4*ZaNYU1^JQM`JT9NDRc!NCK+O9Ex_r$06}p_? zTb!*bx_s>EH|TPHgR$ls`e^PoIQjU7-=fRi1@XLfbot@)?qSQv^ES}sJa3I2E+RL9 zb?CDOzxnTh_rC+YzqWDuk0E~nv%tQ4{~6@3Kt8@@9o@H#Z+{=%7{5>C51MHXy)) literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..6bc20349bf49ebb2bc6b51e63c68c58884199e8d GIT binary patch literal 1264 zcmZ9LO>fgc5QaCg-IRuYQQA@pQA&@XiUS}dBoIR29$W>9kE>-8t2J`sC~*+I@!R-S zT#$I5bv9kG(PVaJ-g#$db{(`&J0@zGuGusXO|y1Q+qAUpnl-EA0lE`9gDeTbG%rc_SDU?G8{2X3i51=x`*NEa2imX6*OwgX?7VAi-e6z< z>(YEF_+Kq;-Xp%pvcYahjQREEiBRCW5`4bEeOo^89uA zju_|Jk#0$daUN{$7T=M+lsnRJV6*<$$Ev<3KREdA0@A_fgc5QaCg-IRuYQQA@pQA&@XiUS}dBoIR29$W>9kE>-8t2J`sC~*+I@!R-S zT#$I5bv9kG(PVaJ-g#$db{(`&J0@zGuGusXO|y1Q+qAUpnl-EA0lE`9gDeTbG%rc_SDU?G8{2X3i51=x`*NEa2imX6*OwgX?7VAi-e6z< z>(YEF_+Kq;-Xp%pvcYahjQREEiBRCW5`4bEeOo^89uA zju_|Jk#0$daUN{$7T=M+lsnRJV6*<$$Ev<3KREdA0@A_> 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 0000000000000000000000000000000000000000..1aa3a6e8b8b32803dbccd3294b4812491f1c1c45 GIT binary patch literal 16300 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..61a69b16435d25d76d3031cc5e3fbc36c6d43d59 GIT binary patch literal 1264 zcmZ9LPjAye5XCpK-IRv@qqLlA_-uSC zE=auJ+MBG{Xfiu9Z{F<8u7l=j%R~*+Hk;<5sn)J(nufM*vu1Uid`iw1<>>6$^QR!T zOkjavwuK6%kx80Q3EYzGO7kT=aN3PnTTq({oRAcw3HBQOVOX2fi;!Z;I=( z|Mh!ShZyT!F)rrPkt#8>B}av~^_XLlUwtck=}cS8ec$tW)V42TET5Zua2z$YiR(Fa zELwBW-7qJ5B}SjCSZI4ZSz;^u(b5iVzvP}P*Y!tF=Q^>oPdz*_gj#60nZ2`D+@XC- zer;w#yut^Do?UI%zpL$Rzc%x5wb}c%u^soGSm8afFSn|3p#7SBUCE)&&b!9u4R-av zF3p#M|5a`C9`QYv4R%9f%&#|3gaX%=;PVCU+wy@YcVG6VbXUS6z9mhZGj=p*?vEUB z#5m86bVEXn^I&tg_>T0Y+>wR@oAtjwR`osk!NGSIkQP4puzCNijR literal 0 HcmV?d00001 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 .