
.. _program_listing_file_Src_GraphicsEngineVulkan_scene_sky_box_SkyBox.cpp:

Program Listing for File SkyBox.cpp
===================================

|exhale_lsh| :ref:`Return to documentation for file <file_Src_GraphicsEngineVulkan_scene_sky_box_SkyBox.cpp>` (``Src/GraphicsEngineVulkan/scene/sky_box/SkyBox.cpp``)

.. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS

.. code-block:: cpp

   module;
   #include <memory>
   #include <array>
   #include <filesystem>
   #include <sstream>
   #include <string>
   #include <vector>
   #include <vulkan/vulkan.hpp>
   #include <stb_image.h>
   #include <spdlog/spdlog.h>
   #include <glm/glm.hpp>
   #include "shared/scene/Vertex.hpp"
   #include "shared/scene/ObjMaterial.hpp"
   
   #include "common/Utilities.hpp"
   
   module kataglyphis.vulkan.sky_box;
   
   import kataglyphis.vulkan.vertex;
   import kataglyphis.vulkan.texture;
   import kataglyphis.vulkan.mesh;
   import kataglyphis.vulkan.file;
   import kataglyphis.vulkan.shader_helper;
   import kataglyphis.vulkan.buffer;
   import kataglyphis.vulkan.command_buffer_manager;
   
   namespace Kataglyphis {
   
   SkyBox::SkyBox() = default;
   
   void SkyBox::init(std::shared_ptr<VulkanDevice>in_device, vk::CommandPool commandPool)
   {
       this->device = in_device;
   
       createMesh(commandPool);
       loadCubeMap(commandPool);
   }
   
   void SkyBox::loadCubeMap(vk::CommandPool commandPool)
   {
       std::stringstream skybox_base_dir;
       std::filesystem::path const cwd = std::filesystem::current_path();
       skybox_base_dir << cwd.string();
       skybox_base_dir << "/Resources/Textures/Skybox/DOOM2016/";
   
       std::array<std::string, 6> skybox_textures = {
           "DOOM16RT.png", "DOOM16LF.png", "DOOM16UP.png", "DOOM16DN.png", "DOOM16FT.png", "DOOM16BK.png"
       };
   
       cubeMapTexture = std::make_unique<Texture>();
   
       int width = 0, height = 0, bit_depth = 0;
       std::vector<unsigned char*> face_data(6);
       vk::DeviceSize layerSize = 0;
       vk::DeviceSize imageSize = 0;
   
       for (size_t i = 0; i < 6; i++) {
           std::string path = skybox_base_dir.str() + skybox_textures[i];
           face_data[i] = stbi_load(path.c_str(), &width, &height, &bit_depth, 4);
           if (!face_data[i]) {
               spdlog::error("Failed to load skybox texture: {}", path);
               for (size_t j = 0; j < i; j++) { stbi_image_free(face_data[j]); }
               return;
           }
           layerSize = static_cast<vk::DeviceSize>(width) * static_cast<vk::DeviceSize>(height) * 4;
           imageSize += layerSize;
       }
   
       spdlog::info("SkyBox: All 6 textures loaded, width={}, height={}, totalImageSize={}", width, height, imageSize);
   
       cubeMapTexture->createImage(device, static_cast<uint32_t>(width), static_cast<uint32_t>(height), 1, vk::Format::eR8G8B8A8Unorm, vk::ImageTiling::eOptimal, vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferDst, vk::MemoryPropertyFlagBits::eDeviceLocal, 6, vk::ImageCreateFlagBits::eCubeCompatible);
   
       VulkanBuffer stagingBuffer;
       stagingBuffer.create(device, imageSize, vk::BufferUsageFlagBits::eTransferSrc, vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent);
   
       void* mappedData = device->getLogicalDevice().mapMemory(stagingBuffer.getBufferMemory(), 0, imageSize).value;
       for (size_t i = 0; i < 6; i++) {
           void* layerOffset = static_cast<char*>(mappedData) + i * layerSize;
           std::memcpy(layerOffset, face_data[i], static_cast<size_t>(layerSize));
       }
       device->getLogicalDevice().unmapMemory(stagingBuffer.getBufferMemory());
   
       for (size_t i = 0; i < 6; i++) { stbi_image_free(face_data[i]); }
   
       vk::CommandBuffer commandBuffer = Kataglyphis::VulkanRendererInternals::CommandBufferManager::beginCommandBuffer(device->getLogicalDevice(), commandPool);
   
       vk::ImageMemoryBarrier barrier{};
       barrier.srcQueueFamilyIndex = vk::QueueFamilyIgnored;
       barrier.dstQueueFamilyIndex = vk::QueueFamilyIgnored;
       barrier.image = cubeMapTexture->getImage();
       barrier.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor;
       barrier.subresourceRange.baseMipLevel = 0;
       barrier.subresourceRange.levelCount = 1;
       barrier.subresourceRange.baseArrayLayer = 0;
       barrier.subresourceRange.layerCount = 6;
   
       barrier.oldLayout = vk::ImageLayout::eUndefined;
       barrier.newLayout = vk::ImageLayout::eTransferDstOptimal;
       barrier.srcAccessMask = vk::AccessFlags{};
       barrier.dstAccessMask = vk::AccessFlagBits::eTransferWrite;
   
       commandBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe, vk::PipelineStageFlagBits::eTransfer, vk::DependencyFlags{}, {}, {}, barrier);
   
       vk::BufferImageCopy region{};
       region.bufferOffset = 0;
       region.bufferRowLength = static_cast<uint32_t>(width);
       region.bufferImageHeight = static_cast<uint32_t>(height);
       region.imageSubresource.aspectMask = vk::ImageAspectFlagBits::eColor;
       region.imageSubresource.mipLevel = 0;
       region.imageSubresource.baseArrayLayer = 0;
       region.imageSubresource.layerCount = 6;
       region.imageOffset = vk::Offset3D{0, 0, 0};
       region.imageExtent = vk::Extent3D{static_cast<uint32_t>(width), static_cast<uint32_t>(height), 1};
   
       commandBuffer.copyBufferToImage(stagingBuffer.getBuffer(), cubeMapTexture->getImage(), vk::ImageLayout::eTransferDstOptimal, 1, &region);
   
       barrier.oldLayout = vk::ImageLayout::eTransferDstOptimal;
       barrier.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
       barrier.srcAccessMask = vk::AccessFlagBits::eTransferWrite;
       barrier.dstAccessMask = vk::AccessFlagBits::eShaderRead;
   
       commandBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eFragmentShader, vk::DependencyFlags{}, {}, {}, barrier);
   
       Kataglyphis::VulkanRendererInternals::CommandBufferManager::endAndSubmitCommandBuffer(device->getLogicalDevice(), commandPool, device->getGraphicsQueue(), commandBuffer);
   
       stagingBuffer.cleanUp();
   
       cubeMapTexture->createImageView(device, vk::Format::eR8G8B8A8Unorm, vk::ImageAspectFlagBits::eColor, 1, vk::ImageViewType::eCube, 6);
       cubeMapTexture->createTextureSampler(device);
   
       createDescriptorSetForCubeMap();
   }
   
   void SkyBox::createDescriptorSetForCubeMap()
   {
       vk::DescriptorSetLayoutBinding cubemapBinding{};
       cubemapBinding.binding = 1;
       cubemapBinding.descriptorCount = 1;
       cubemapBinding.descriptorType = vk::DescriptorType::eCombinedImageSampler;
       cubemapBinding.stageFlags = vk::ShaderStageFlagBits::eFragment;
   
       vk::DescriptorSetLayoutCreateInfo layoutInfo{};
       layoutInfo.bindingCount = 1;
       layoutInfo.pBindings = &cubemapBinding;
   
       auto layoutResult = device->getLogicalDevice().createDescriptorSetLayout(layoutInfo);
       ASSERT_VULKAN(VkResult(layoutResult.result), "Failed to create skybox descriptor set layout!");
       descriptorSetLayout = layoutResult.value;
   
       vk::DescriptorPoolSize poolSize{};
       poolSize.type = vk::DescriptorType::eCombinedImageSampler;
       poolSize.descriptorCount = 1;
   
       vk::DescriptorPoolCreateInfo poolInfo{};
       poolInfo.poolSizeCount = 1;
       poolInfo.pPoolSizes = &poolSize;
       poolInfo.maxSets = 1;
   
       auto poolResult = device->getLogicalDevice().createDescriptorPool(poolInfo);
       ASSERT_VULKAN(VkResult(poolResult.result), "Failed to create skybox descriptor pool!");
       descriptorPool = poolResult.value;
   
       vk::DescriptorSetAllocateInfo allocInfo{};
       allocInfo.descriptorPool = descriptorPool;
       allocInfo.descriptorSetCount = 1;
       allocInfo.pSetLayouts = &descriptorSetLayout;
   
       auto allocResult = device->getLogicalDevice().allocateDescriptorSets(allocInfo);
       ASSERT_VULKAN(VkResult(allocResult.result), "Failed to allocate skybox descriptor set!");
       descriptorSet = allocResult.value[0];
   
       vk::DescriptorImageInfo imageInfo{};
       imageInfo.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal;
       imageInfo.imageView = cubeMapTexture->getImageView();
       imageInfo.sampler = cubeMapTexture->getSampler();
   
       vk::WriteDescriptorSet write{};
       write.dstSet = descriptorSet;
       write.dstBinding = 1;
       write.dstArrayElement = 0;
       write.descriptorType = vk::DescriptorType::eCombinedImageSampler;
       write.descriptorCount = 1;
       write.pImageInfo = &imageInfo;
   
       device->getLogicalDevice().updateDescriptorSets(1, &write, 0, nullptr);
   }
   
   void SkyBox::createRenderPass(vk::Format format, vk::Format depthFormat)
   {
       vk::AttachmentDescription colorAttachment{};
       colorAttachment.format = format;
       colorAttachment.samples = vk::SampleCountFlagBits::e1;
       colorAttachment.loadOp = vk::AttachmentLoadOp::eClear;
       colorAttachment.storeOp = vk::AttachmentStoreOp::eStore;
       colorAttachment.stencilLoadOp = vk::AttachmentLoadOp::eDontCare;
       colorAttachment.stencilStoreOp = vk::AttachmentStoreOp::eDontCare;
       colorAttachment.initialLayout = vk::ImageLayout::eUndefined;
       colorAttachment.finalLayout = vk::ImageLayout::eColorAttachmentOptimal;
   
       vk::AttachmentDescription depthAttachment{};
       depthAttachment.format = depthFormat;
       depthAttachment.samples = vk::SampleCountFlagBits::e1;
       depthAttachment.loadOp = vk::AttachmentLoadOp::eClear;
       depthAttachment.storeOp = vk::AttachmentStoreOp::eStore;
       depthAttachment.stencilLoadOp = vk::AttachmentLoadOp::eDontCare;
       depthAttachment.stencilStoreOp = vk::AttachmentStoreOp::eDontCare;
       depthAttachment.initialLayout = vk::ImageLayout::eUndefined;
       depthAttachment.finalLayout = vk::ImageLayout::eDepthStencilAttachmentOptimal;
   
       std::array attachments = {colorAttachment, depthAttachment};
   
       vk::AttachmentReference colorRef{};
       colorRef.attachment = 0;
       colorRef.layout = vk::ImageLayout::eColorAttachmentOptimal;
   
       vk::AttachmentReference depthRef{};
       depthRef.attachment = 1;
       depthRef.layout = vk::ImageLayout::eDepthStencilAttachmentOptimal;
   
       vk::SubpassDescription subpass{};
       subpass.pipelineBindPoint = vk::PipelineBindPoint::eGraphics;
       subpass.colorAttachmentCount = 1;
       subpass.pColorAttachments = &colorRef;
       subpass.pDepthStencilAttachment = &depthRef;
   
       std::array<vk::SubpassDependency, 2> dependencies{};
   
       dependencies[0].srcSubpass = VK_SUBPASS_EXTERNAL;
       dependencies[0].dstSubpass = 0;
       dependencies[0].srcStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput;
       dependencies[0].dstStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput;
       dependencies[0].srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite;
       dependencies[0].dstAccessMask = vk::AccessFlagBits::eColorAttachmentWrite | vk::AccessFlagBits::eColorAttachmentRead;
       dependencies[0].dependencyFlags = vk::DependencyFlagBits::eByRegion;
   
       dependencies[1].srcSubpass = 0;
       dependencies[1].dstSubpass = VK_SUBPASS_EXTERNAL;
       dependencies[1].srcStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput;
       dependencies[1].dstStageMask = vk::PipelineStageFlagBits::eColorAttachmentOutput;
       dependencies[1].srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite;
       dependencies[1].dstAccessMask = vk::AccessFlagBits::eColorAttachmentRead;
       dependencies[1].dependencyFlags = vk::DependencyFlagBits::eByRegion;
   
       vk::RenderPassCreateInfo renderPassInfo{};
       renderPassInfo.attachmentCount = 2;
       renderPassInfo.pAttachments = attachments.data();
       renderPassInfo.subpassCount = 1;
       renderPassInfo.pSubpasses = &subpass;
       renderPassInfo.dependencyCount = static_cast<uint32_t>(dependencies.size());
       renderPassInfo.pDependencies = dependencies.data();
   
       auto result = device->getLogicalDevice().createRenderPass(renderPassInfo);
       ASSERT_VULKAN(VkResult(result.result), "Failed to create skybox render pass!");
       renderPass = result.value;
   }
   
   void SkyBox::createFramebuffers(size_t count, const std::vector<vk::ImageView>& imageViews, const std::vector<vk::ImageView>& depthViews, uint32_t width, uint32_t height)
   {
       framebufferWidth = width;
       framebufferHeight = height;
       framebuffers.resize(count);
       for (size_t i = 0; i < count; i++) {
           std::array attachments = {imageViews[i], depthViews[i]};
           vk::FramebufferCreateInfo fbInfo{};
           fbInfo.renderPass = renderPass;
           fbInfo.attachmentCount = 2;
           fbInfo.pAttachments = attachments.data();
           fbInfo.width = width;
           fbInfo.height = height;
           fbInfo.layers = 1;
           auto fbResult = device->getLogicalDevice().createFramebuffer(fbInfo);
           ASSERT_VULKAN(VkResult(fbResult.result), "Failed to create skybox framebuffer!");
           framebuffers[i] = fbResult.value;
       }
   }
   
   void SkyBox::createGraphicsPipeline(vk::DescriptorSetLayout sharedLayout)
   {
       std::stringstream skybox_shader_dir;
       std::filesystem::path const cwd = std::filesystem::current_path();
       skybox_shader_dir << cwd.string() << RELATIVE_RESOURCE_PATH << "Shaders/skybox/";
   
       ShaderHelper shaderHelper;
       shaderHelper.compileShader(skybox_shader_dir.str(), "SkyBox.vert");
       shaderHelper.compileShader(skybox_shader_dir.str(), "SkyBox.frag");
   
       File vertexFile(shaderHelper.getShaderSpvDir(skybox_shader_dir.str(), "SkyBox.vert"));
       File fragmentFile(shaderHelper.getShaderSpvDir(skybox_shader_dir.str(), "SkyBox.frag"));
       std::vector<char> const vertexShaderCode = vertexFile.readCharSequence();
       std::vector<char> const fragmentShaderCode = fragmentFile.readCharSequence();
   
       vk::ShaderModule vertexShaderModule = shaderHelper.createShaderModule(device, vertexShaderCode);
       vk::ShaderModule fragmentShaderModule = shaderHelper.createShaderModule(device, fragmentShaderCode);
   
       vk::PipelineShaderStageCreateInfo vertStageInfo{};
       vertStageInfo.stage = vk::ShaderStageFlagBits::eVertex;
       vertStageInfo.module = vertexShaderModule;
       vertStageInfo.pName = "main";
   
       vk::PipelineShaderStageCreateInfo fragStageInfo{};
       fragStageInfo.stage = vk::ShaderStageFlagBits::eFragment;
       fragStageInfo.module = fragmentShaderModule;
       fragStageInfo.pName = "main";
   
       std::array skyStages = {vertStageInfo, fragStageInfo};
   
       vk::VertexInputBindingDescription bindingDescription{};
       bindingDescription.binding = 0;
       bindingDescription.stride = sizeof(Vertex);
       bindingDescription.inputRate = vk::VertexInputRate::eVertex;
   
       std::array<vk::VertexInputAttributeDescription, 4> attributeDescriptions = vertex::getVertexInputAttributeDesc();
   
       vk::PipelineVertexInputStateCreateInfo vertexInputInfo{};
       vertexInputInfo.vertexBindingDescriptionCount = 1;
       vertexInputInfo.pVertexBindingDescriptions = &bindingDescription;
       vertexInputInfo.vertexAttributeDescriptionCount = static_cast<uint32_t>(attributeDescriptions.size());
       vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data();
   
       vk::PipelineInputAssemblyStateCreateInfo inputAssembly{};
       inputAssembly.topology = vk::PrimitiveTopology::eTriangleList;
       inputAssembly.primitiveRestartEnable = VK_FALSE;
   
       vk::PipelineDepthStencilStateCreateInfo depthStencil{};
       depthStencil.depthTestEnable = VK_FALSE;
       depthStencil.depthWriteEnable = VK_FALSE;
       depthStencil.depthCompareOp = vk::CompareOp::eAlways;
       depthStencil.depthBoundsTestEnable = VK_FALSE;
       depthStencil.stencilTestEnable = VK_FALSE;
   
       vk::PipelineColorBlendAttachmentState colorBlendAttachment{};
       colorBlendAttachment.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG | vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA;
       colorBlendAttachment.blendEnable = VK_FALSE;
   
       vk::PipelineColorBlendStateCreateInfo colorBlending{};
       colorBlending.logicOpEnable = VK_FALSE;
       colorBlending.attachmentCount = 1;
       colorBlending.pAttachments = &colorBlendAttachment;
   
       std::vector<vk::DynamicState> dynamicStates = {vk::DynamicState::eViewport, vk::DynamicState::eScissor};
       vk::PipelineDynamicStateCreateInfo dynamicState{};
       dynamicState.dynamicStateCount = static_cast<uint32_t>(dynamicStates.size());
       dynamicState.pDynamicStates = dynamicStates.data();
   
       vk::PipelineRasterizationStateCreateInfo rasterizer{};
       rasterizer.depthClampEnable = VK_FALSE;
       rasterizer.rasterizerDiscardEnable = VK_FALSE;
       rasterizer.polygonMode = vk::PolygonMode::eFill;
       rasterizer.lineWidth = 1.0f;
       rasterizer.cullMode = vk::CullModeFlagBits::eNone;
       rasterizer.depthBiasEnable = VK_FALSE;
   
       vk::PipelineMultisampleStateCreateInfo multisampling{};
       multisampling.sampleShadingEnable = VK_FALSE;
       multisampling.rasterizationSamples = vk::SampleCountFlagBits::e1;
   
       vk::PipelineViewportStateCreateInfo viewportState{};
       viewportState.viewportCount = 1;
       viewportState.scissorCount = 1;
   
       std::array<vk::DescriptorSetLayout, 2> combinedLayouts = {sharedLayout, descriptorSetLayout};
       vk::PipelineLayoutCreateInfo pipelineLayoutInfo{};
       pipelineLayoutInfo.setLayoutCount = 2;
       pipelineLayoutInfo.pSetLayouts = combinedLayouts.data();
   
       vk::PushConstantRange pushConstantRange{};
       pushConstantRange.stageFlags = vk::ShaderStageFlagBits::eFragment;
       pushConstantRange.offset = 0;
       pushConstantRange.size = sizeof(uint32_t);
       pipelineLayoutInfo.pushConstantRangeCount = 1;
       pipelineLayoutInfo.pPushConstantRanges = &pushConstantRange;
   
       auto layoutRes = device->getLogicalDevice().createPipelineLayout(pipelineLayoutInfo);
       ASSERT_VULKAN(VkResult(layoutRes.result), "Failed to create skybox pipeline layout!");
       pipelineLayout = layoutRes.value;
   
       vk::GraphicsPipelineCreateInfo pipelineInfo{};
       pipelineInfo.stageCount = static_cast<uint32_t>(skyStages.size());
       pipelineInfo.pStages = skyStages.data();
       pipelineInfo.pVertexInputState = &vertexInputInfo;
       pipelineInfo.pInputAssemblyState = &inputAssembly;
       pipelineInfo.pViewportState = &viewportState;
       pipelineInfo.pDynamicState = &dynamicState;
       pipelineInfo.pRasterizationState = &rasterizer;
       pipelineInfo.pMultisampleState = &multisampling;
       pipelineInfo.pDepthStencilState = &depthStencil;
       pipelineInfo.pColorBlendState = &colorBlending;
       pipelineInfo.layout = pipelineLayout;
       pipelineInfo.renderPass = renderPass;
       pipelineInfo.subpass = 0;
   
       auto pipelineRes = device->getLogicalDevice().createGraphicsPipeline(nullptr, pipelineInfo);
       ASSERT_VULKAN(VkResult(pipelineRes.result), "Failed to create skybox graphics pipeline!");
       graphicsPipeline = pipelineRes.value;
   
       device->getLogicalDevice().destroyShaderModule(vertexShaderModule);
       device->getLogicalDevice().destroyShaderModule(fragmentShaderModule);
   }
   
   void SkyBox::createMesh(vk::CommandPool commandPool)
   {
       // Fullscreen quad indices
       std::vector<unsigned int> indices = {
           0, 1, 2,
           2, 1, 3
       };
   
       // Fullscreen quad vertices with UVs
       std::vector<Vertex> vertices = {
           Vertex(glm::vec3(-1.0F, -1.0F, 0.0F), glm::vec3(0), glm::vec3(0), glm::vec2(0.0F, 0.0F)),
           Vertex(glm::vec3( 1.0F, -1.0F, 0.0F), glm::vec3(0), glm::vec3(0), glm::vec2(1.0F, 0.0F)),
           Vertex(glm::vec3(-1.0F,  1.0F, 0.0F), glm::vec3(0), glm::vec3(0), glm::vec2(0.0F, 1.0F)),
           Vertex(glm::vec3( 1.0F,  1.0F, 0.0F), glm::vec3(0), glm::vec3(0), glm::vec2(1.0F, 1.0F))
       };
   
       skyMesh = std::make_unique<Kataglyphis::Mesh>();
       std::vector<unsigned int> materialIndex = {0};
       std::vector<ObjMaterial> materials = {ObjMaterial{}};
       skyMesh = std::make_unique<Mesh>(device, device->getGraphicsQueue(), commandPool, vertices, indices, materialIndex, materials);
   }
   
   void SkyBox::recordCommands(vk::CommandBuffer &commandBuffer, uint32_t image_index, const std::vector<vk::DescriptorSet> &descriptorSets, bool skyboxEnabled)
   {
       if (image_index >= framebuffers.size() || framebuffers.empty()) {
           spdlog::error("SkyBox: framebuffer not created or index out of range!");
           return;
       }
   
       spdlog::debug("SkyBox: enabled={}, fbSize={}, indexCount={}", skyboxEnabled, framebuffers.size(), skyMesh->getIndexCount());
   
       vk::RenderPassBeginInfo renderPassInfo{};
       renderPassInfo.renderPass = renderPass;
       renderPassInfo.framebuffer = framebuffers[image_index];
       renderPassInfo.renderArea.offset = vk::Offset2D{0, 0};
       renderPassInfo.renderArea.extent = vk::Extent2D{framebufferWidth, framebufferHeight};
   
       std::array clearValues = {
           vk::ClearValue{std::array<float, 4>{0.0f, 0.0f, 0.0f, 1.0f}},
           vk::ClearValue{vk::ClearDepthStencilValue{1.0f, 0}}
       };
       renderPassInfo.clearValueCount = 2;
       renderPassInfo.pClearValues = clearValues.data();
   
       commandBuffer.beginRenderPass(renderPassInfo, vk::SubpassContents::eInline);
   
       vk::Viewport viewport{};
       viewport.x = 0.0f;
       viewport.y = 0.0f;
       viewport.width = static_cast<float>(framebufferWidth);
       viewport.height = static_cast<float>(framebufferHeight);
       viewport.minDepth = 0.0f;
       viewport.maxDepth = 1.0f;
       commandBuffer.setViewport(0, 1, &viewport);
   
       vk::Rect2D scissor{};
       scissor.offset = vk::Offset2D{ 0, 0 };
       scissor.extent = vk::Extent2D{ framebufferWidth, framebufferHeight };
       commandBuffer.setScissor(0, 1, &scissor);
   
       commandBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, graphicsPipeline);
   
       std::vector<vk::DescriptorSet> skyboxDescriptorSets = {descriptorSets[0], descriptorSet};
       commandBuffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipelineLayout, 0, skyboxDescriptorSets, nullptr);
   
       uint32_t skyboxEnabledVal = skyboxEnabled ? 1u : 0u;
       commandBuffer.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eFragment, 0, sizeof(uint32_t), &skyboxEnabledVal);
   
       std::vector<vk::Buffer> const vertex_buffers = { skyMesh->getVertexBuffer() };
       vk::DeviceSize offsets[] = { 0 };
       commandBuffer.bindVertexBuffers(0, vertex_buffers, offsets);
       commandBuffer.bindIndexBuffer(skyMesh->getIndexBuffer(), 0, vk::IndexType::eUint32);
   
       commandBuffer.drawIndexed(skyMesh->getIndexCount(), 1, 0, 0, 0);
       commandBuffer.endRenderPass();
   }
   
   void SkyBox::cleanUp()
   {
       if (device) {
           for (auto fb : framebuffers) {
               device->getLogicalDevice().destroyFramebuffer(fb);
           }
           framebuffers.clear();
           if (graphicsPipeline) { device->getLogicalDevice().destroyPipeline(graphicsPipeline); }
           if (pipelineLayout) { device->getLogicalDevice().destroyPipelineLayout(pipelineLayout); }
           if (descriptorSetLayout) { device->getLogicalDevice().destroyDescriptorSetLayout(descriptorSetLayout); }
           if (descriptorPool) { device->getLogicalDevice().destroyDescriptorPool(descriptorPool); }
           if (renderPass) { device->getLogicalDevice().destroyRenderPass(renderPass); }
       }
       if (skyMesh) {
           skyMesh->cleanUp();
       }
       if (cubeMapTexture) {
           cubeMapTexture->cleanUp();
       }
   }
   
   SkyBox::~SkyBox() = default;
   
   void SkyBox::destroyFramebuffers()
   {
       for (auto fb : framebuffers) { device->getLogicalDevice().destroyFramebuffer(fb); }
       framebuffers.clear();
   }
   
   void SkyBox::recreateFrameResources(size_t count, const std::vector<vk::ImageView>& imageViews, const std::vector<vk::ImageView>& depthViews, uint32_t width, uint32_t height)
   {
       createFramebuffers(count, imageViews, depthViews, width, height);
   }
   
   }
