diff --git a/include/gfx_device.hpp b/include/gfx_device.hpp index c53e820..6fdda92 100644 --- a/include/gfx_device.hpp +++ b/include/gfx_device.hpp @@ -23,7 +23,7 @@ namespace engine { // adds a draw call to the queue // vertexBuffer is required, indexBuffer can be NULL, uniformData is required - void draw(const gfx::Pipeline* pipeline, const gfx::Buffer* vertexBuffer, const gfx::Buffer* indexBuffer, uint32_t count, const void* pushConstantData, size_t pushConstantSize); + void draw(const gfx::Pipeline* pipeline, const gfx::Buffer* vertexBuffer, const gfx::Buffer* indexBuffer, uint32_t count, const void* pushConstantData, size_t pushConstantSize, const gfx::Texture* texture); // Call once per frame. Executes all queued draw calls and renders to the screen. void renderFrame(); diff --git a/include/resources/texture.hpp b/include/resources/texture.hpp index 4b79514..40d8586 100644 --- a/include/resources/texture.hpp +++ b/include/resources/texture.hpp @@ -14,6 +14,8 @@ public: Texture(const std::filesystem::path& resPath); ~Texture() override; + gfx::Texture* getHandle(); + private: gfx::Texture* m_gpuTexture; }; diff --git a/src/components/camera.cpp b/src/components/camera.cpp index fcea4f5..ed9c246 100644 --- a/src/components/camera.cpp +++ b/src/components/camera.cpp @@ -77,7 +77,6 @@ void Camera::usePerspective(float fovDeg) float fovRad = glm::radians(fovDeg); m_projMatrix = glm::perspectiveFovRH_ZO(fovRad, viewportDim.x, viewportDim.y, NEAR, FAR); - m_projMatrix[1][1] *= -1; } void Camera::useOrtho() @@ -88,7 +87,6 @@ void Camera::useOrtho() float aspect = viewportDim.x / viewportDim.y; m_projMatrix = glm::orthoRH_ZO(-10.0f * aspect, 10.0f * aspect, -10.0f, 10.0f, -100.0f, 100.0f); - m_projMatrix[1][1] *= -1; } } diff --git a/src/components/mesh_renderer.cpp b/src/components/mesh_renderer.cpp index 02962b2..312f2d2 100644 --- a/src/components/mesh_renderer.cpp +++ b/src/components/mesh_renderer.cpp @@ -17,7 +17,7 @@ namespace engine::components { Renderer::Renderer(Object* parent) : Component(parent, TypeEnum::RENDERER) { m_shader = this->parent.res.get("shader.glsl"); -// m_texture = this->parent.res.get("textures/missing.png"); + m_texture = this->parent.res.get("textures/missing.png"); } Renderer::~Renderer() @@ -32,7 +32,7 @@ void Renderer::render(glm::mat4 transform, glm::mat4 view) gfxdev->updateUniformBuffer(m_shader->getPipeline(), &uniformData.color, sizeof(uniformData.color), offsetof(resources::Shader::UniformBuffer, color)); glm::mat4 pushConsts[] = { transform, view }; - gfxdev->draw(m_shader->getPipeline(), m_mesh->vb, m_mesh->ib, m_mesh->m_vertices.size(), pushConsts, sizeof(glm::mat4) * 2); + gfxdev->draw(m_shader->getPipeline(), m_mesh->vb, m_mesh->ib, m_mesh->m_indices.size(), pushConsts, sizeof(glm::mat4) * 2, m_texture->getHandle()); } void Renderer::setMesh(const std::string& name) diff --git a/src/gfx_device_vulkan.cpp b/src/gfx_device_vulkan.cpp index 8eef31b..54c6e8e 100644 --- a/src/gfx_device_vulkan.cpp +++ b/src/gfx_device_vulkan.cpp @@ -58,7 +58,6 @@ namespace engine { VkQueue handle; }; - struct DepthBuffer { VkImage image; VmaAllocation allocation; @@ -91,6 +90,7 @@ namespace engine { const gfx::Buffer* indexBuffer = nullptr; // if this is nullptr, don't use indexed uint32_t count = 0; uint8_t pushConstantData[PUSH_CONSTANT_MAX_SIZE]; + const gfx::Texture* texture = nullptr; }; enum class QueueFlags : uint32_t { @@ -119,6 +119,11 @@ namespace engine { struct gfx::Texture { VkImage image; VmaAllocation alloc; + VkImageView imageView; + VkSampler sampler; + VkDescriptorPool pool; + std::array descriptorSets{}; + uint32_t mipLevels; }; @@ -164,7 +169,6 @@ namespace engine { options.SetOptimizationLevel(shaderc_optimization_level_size); options.SetTargetSpirv(shaderc_spirv_version_1_6); options.SetAutoBindUniforms(false); - options.SetInvertY(false); // preprocess shaderc::PreprocessedSourceCompilationResult preprocessed = compiler.PreprocessGlsl(source, kind, filename, options); @@ -774,7 +778,7 @@ namespace engine { vkFreeCommandBuffers(device, commandPool, 1, &commandBuffer); } - static void cmdTransitionImageLayout(VkCommandBuffer commandBuffer, VkImageLayout oldLayout, VkImageLayout newLayout, VkImage image) + static void cmdTransitionImageLayout(VkCommandBuffer commandBuffer, VkImageLayout oldLayout, VkImageLayout newLayout, uint32_t mipLevels, VkImage image) { VkImageMemoryBarrier barrier{ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; @@ -785,7 +789,7 @@ namespace engine { barrier.image = image; barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; barrier.subresourceRange.baseMipLevel = 0; - barrier.subresourceRange.levelCount = 1; + barrier.subresourceRange.levelCount = mipLevels; barrier.subresourceRange.baseArrayLayer = 0; barrier.subresourceRange.layerCount = 1; @@ -814,6 +818,77 @@ namespace engine { } + static void cmdGenerateMipmaps(VkCommandBuffer commandBuffer, VkImage image, int32_t width, int32_t height, uint32_t mipLevels) + { + + VkImageMemoryBarrier barrier{ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; + barrier.image = image; + barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; + barrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + barrier.subresourceRange.baseArrayLayer = 0; + barrier.subresourceRange.layerCount = 1; + barrier.subresourceRange.levelCount = 1; + + int32_t mipWidth = width; + int32_t mipHeight = height; + for (uint32_t i = 1; i < mipLevels; i++) { + barrier.subresourceRange.baseMipLevel = i - 1; + barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + barrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + + vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, + 0, nullptr, + 0, nullptr, + 1, &barrier); + + VkImageBlit blit{}; + blit.srcOffsets[0] = { 0, 0, 0 }; + blit.srcOffsets[1] = { mipWidth, mipHeight, 1 }; + blit.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + blit.srcSubresource.mipLevel = i - 1; + blit.srcSubresource.baseArrayLayer = 0; + blit.srcSubresource.layerCount = 1; + blit.dstOffsets[0] = { 0, 0, 0 }; + blit.dstOffsets[1] = { mipWidth > 1 ? mipWidth / 2 : 1, mipHeight > 1 ? mipHeight / 2 : 1, 1 }; + blit.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + blit.dstSubresource.mipLevel = i; + blit.dstSubresource.baseArrayLayer = 0; + blit.dstSubresource.layerCount = 1; + + vkCmdBlitImage(commandBuffer, image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &blit, VK_FILTER_LINEAR); + + barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; + barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + barrier.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT; + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + + vkCmdPipelineBarrier(commandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, + 0, + 0, nullptr, + 0, nullptr, + 1, &barrier); + + if (mipWidth > 1) mipWidth /= 2; + if (mipHeight > 1) mipHeight /= 2; + + } + + barrier.subresourceRange.baseMipLevel = mipLevels - 1; + barrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; + barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + + vkCmdPipelineBarrier(commandBuffer, + VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, + 0, nullptr, + 0, nullptr, + 1, &barrier); + } + // class definitions struct GFXDevice::Impl { @@ -845,7 +920,10 @@ namespace engine { std::map> drawQueues{}; VkDescriptorSetLayoutBinding uboLayoutBinding{}; - VkDescriptorSetLayout uboLayout{}; + VkDescriptorSetLayout descriptorSetLayout{}; + + VkDescriptorSetLayoutBinding samplerLayoutBinding{}; + VkDescriptorSetLayout samplerSetLayout{}; }; @@ -1032,6 +1110,19 @@ namespace engine { continue; } + // check for some features: + VkPhysicalDeviceFeatures devFeatures; + vkGetPhysicalDeviceFeatures(dev, &devFeatures); + // anisotropic filtering is needed + if (devFeatures.samplerAnisotropy == VK_FALSE) continue; + + // check for linear filtering for mipmaps + VkFormatProperties formatProperties{}; + vkGetPhysicalDeviceFormatProperties(dev, VK_FORMAT_R8G8B8A8_SRGB, &formatProperties); + if (!(formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT)) { + continue; + } + pimpl->physicalDevice = dev; break; @@ -1127,6 +1218,9 @@ namespace engine { throw std::runtime_error("The selected queue family does not support this surface"); } + VkPhysicalDeviceFeatures deviceFeatures{}; + deviceFeatures.samplerAnisotropy = VK_TRUE; + VkDeviceCreateInfo deviceCreateInfo{ .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, .pNext = nullptr, @@ -1137,7 +1231,7 @@ namespace engine { // IGNORED: .ppEnabledLayerNames .enabledExtensionCount = (uint32_t)requiredDeviceExtensions.size(), .ppEnabledExtensionNames = requiredDeviceExtensions.data(), - .pEnabledFeatures = nullptr, + .pEnabledFeatures = &deviceFeatures, }; res = vkCreateDevice(pimpl->physicalDevice, &deviceCreateInfo, nullptr, &pimpl->device); @@ -1244,24 +1338,41 @@ namespace engine { assert(res == VK_SUCCESS); } - // create uniform buffer stuff + // create uniform buffer descriptor set layout pimpl->uboLayoutBinding.binding = 0; pimpl->uboLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; pimpl->uboLayoutBinding.descriptorCount = 1; pimpl->uboLayoutBinding.stageFlags = VK_SHADER_STAGE_ALL_GRAPHICS; pimpl->uboLayoutBinding.pImmutableSamplers = nullptr; + VkDescriptorSetLayoutCreateInfo descriptorSetLayoutInfo{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; descriptorSetLayoutInfo.bindingCount = 1; descriptorSetLayoutInfo.pBindings = &pimpl->uboLayoutBinding; - res = vkCreateDescriptorSetLayout(pimpl->device, &descriptorSetLayoutInfo, nullptr, &pimpl->uboLayout); + res = vkCreateDescriptorSetLayout(pimpl->device, &descriptorSetLayoutInfo, nullptr, &pimpl->descriptorSetLayout); assert(res == VK_SUCCESS); + + // create texture sampler descriptor set layout + pimpl->samplerLayoutBinding.binding = 0; + pimpl->samplerLayoutBinding.descriptorCount = 1; + pimpl->samplerLayoutBinding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + pimpl->samplerLayoutBinding.pImmutableSamplers = nullptr; + pimpl->samplerLayoutBinding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; + + VkDescriptorSetLayoutCreateInfo samplerSetLayoutInfo{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; + samplerSetLayoutInfo.bindingCount = 1; + samplerSetLayoutInfo.pBindings = &pimpl->samplerLayoutBinding; + res = vkCreateDescriptorSetLayout(pimpl->device, &samplerSetLayoutInfo, nullptr, &pimpl->samplerSetLayout); + assert(res == VK_SUCCESS); + + } GFXDevice::~GFXDevice() { TRACE("Destroying GFXDevice..."); - vkDestroyDescriptorSetLayout(pimpl->device, pimpl->uboLayout, nullptr); + vkDestroyDescriptorSetLayout(pimpl->device, pimpl->samplerSetLayout, nullptr); + vkDestroyDescriptorSetLayout(pimpl->device, pimpl->descriptorSetLayout, nullptr); for (int i = 0; i < FRAMES_IN_FLIGHT; i++) { @@ -1298,7 +1409,7 @@ namespace engine { *h = (uint32_t)height; } - void GFXDevice::draw(const gfx::Pipeline* pipeline, const gfx::Buffer* vertexBuffer, const gfx::Buffer* indexBuffer, uint32_t count, const void* pushConstantData, size_t pushConstantSize) + void GFXDevice::draw(const gfx::Pipeline* pipeline, const gfx::Buffer* vertexBuffer, const gfx::Buffer* indexBuffer, uint32_t count, const void* pushConstantData, size_t pushConstantSize, const gfx::Texture* texture) { assert(vertexBuffer->type == gfx::BufferType::VERTEX); assert(vertexBuffer != nullptr); @@ -1313,6 +1424,8 @@ namespace engine { memcpy(call.pushConstantData, pushConstantData, pushConstantSize); + call.texture = texture; // will be ignored if nullptr + pimpl->drawQueues[pipeline].push(call); } @@ -1367,9 +1480,9 @@ namespace engine { VkViewport viewport{}; viewport.x = 0.0f; - viewport.y = 0.0f; + viewport.y = (float)pimpl->swapchain.extent.height; viewport.width = (float)pimpl->swapchain.extent.width; - viewport.height = (float)pimpl->swapchain.extent.height; + viewport.height = -(float)pimpl->swapchain.extent.height; viewport.minDepth = 0.0f; viewport.maxDepth = 1.0f; vkCmdSetViewport(pimpl->commandBuffers[frameIndex], 0, 1, &viewport); @@ -1390,6 +1503,8 @@ namespace engine { DrawCall call = queue.front(); + vkCmdBindDescriptorSets(pimpl->commandBuffers[frameIndex], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline->layout, 1, 1, &call.texture->descriptorSets[frameIndex], 0, nullptr); + vkCmdPushConstants(pimpl->commandBuffers[frameIndex], pipeline->layout, VK_SHADER_STAGE_VERTEX_BIT, 0, PUSH_CONSTANT_MAX_SIZE, call.pushConstantData); vkCmdBindVertexBuffers(pimpl->commandBuffers[frameIndex], 0, 1, &call.vertexBuffer->buffer, offsets); @@ -1465,7 +1580,7 @@ namespace engine { VkShaderModule vertShaderModule = compileShader(pimpl->device, shaderc_vertex_shader, vertShaderCode.data(), vertShaderPath); VkShaderModule fragShaderModule = compileShader(pimpl->device, shaderc_fragment_shader, fragShaderCode.data(), fragShaderPath); - + // create uniform buffers pipeline->uniformBuffers.resize(FRAMES_IN_FLIGHT); for (int i = 0; i < FRAMES_IN_FLIGHT; i++) { @@ -1490,10 +1605,11 @@ namespace engine { pipeline->uniformBuffers[i] = buf; } - // create descriptor pool for uniform buffers + // create descriptor pools VkDescriptorPoolSize poolSize{}; poolSize.type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; poolSize.descriptorCount = FRAMES_IN_FLIGHT; + VkDescriptorPoolCreateInfo poolInfo{}; poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; poolInfo.poolSizeCount = 1; @@ -1503,19 +1619,21 @@ namespace engine { assert(res == VK_SUCCESS); std::array layouts; - layouts.fill(pimpl->uboLayout); + layouts.fill(pimpl->descriptorSetLayout); VkDescriptorSetAllocateInfo dSetAllocInfo{}; dSetAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; dSetAllocInfo.descriptorPool = pipeline->descriptorPool; dSetAllocInfo.descriptorSetCount = FRAMES_IN_FLIGHT; dSetAllocInfo.pSetLayouts = layouts.data(); res = vkAllocateDescriptorSets(pimpl->device, &dSetAllocInfo, pipeline->descriptorSets.data()); + assert(res == VK_SUCCESS); for (int i = 0; i < FRAMES_IN_FLIGHT; i++) { VkDescriptorBufferInfo bufferInfo{}; bufferInfo.buffer = pipeline->uniformBuffers[i]->buffer; bufferInfo.offset = 0; bufferInfo.range = uniformBufferSize; + VkWriteDescriptorSet descriptorWrite{}; descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; descriptorWrite.dstSet = pipeline->descriptorSets[i]; @@ -1576,9 +1694,9 @@ namespace engine { VkViewport viewport{}; viewport.x = 0.0f; - viewport.y = 0.0f; + viewport.y = (float)pimpl->swapchain.extent.height; viewport.width = (float)pimpl->swapchain.extent.width; - viewport.height = (float)pimpl->swapchain.extent.height; + viewport.height = -(float)pimpl->swapchain.extent.height; viewport.minDepth = 0.0f; viewport.maxDepth = 1.0f; @@ -1667,10 +1785,12 @@ namespace engine { pushConstantRange.size = PUSH_CONSTANT_MAX_SIZE; pushConstantRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT; + std::array setLayouts{ pimpl->descriptorSetLayout, pimpl->samplerSetLayout}; + VkPipelineLayoutCreateInfo layoutInfo{}; layoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; - layoutInfo.setLayoutCount = 1; - layoutInfo.pSetLayouts = &pimpl->uboLayout; + layoutInfo.setLayoutCount = setLayouts.size(); + layoutInfo.pSetLayouts = setLayouts.data(); layoutInfo.pushConstantRangeCount = 1; layoutInfo.pPushConstantRanges = &pushConstantRange; @@ -1810,6 +1930,8 @@ namespace engine { size_t imageSize = w * h * 4; + out->mipLevels = static_cast(std::floor(std::log2(std::max(w, h)))) + 1; + // first load image into staging buffer VkBuffer stagingBuffer; VmaAllocation stagingAllocation; @@ -1841,12 +1963,12 @@ namespace engine { imageInfo.extent.width = w; imageInfo.extent.height = h; imageInfo.extent.depth = 1; - imageInfo.mipLevels = 1; + imageInfo.mipLevels = out->mipLevels; imageInfo.arrayLayers = 1; imageInfo.format = VK_FORMAT_R8G8B8A8_SRGB; imageInfo.tiling = VK_IMAGE_TILING_OPTIMAL; imageInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; + imageInfo.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_SAMPLED_BIT; imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; imageInfo.flags = 0; @@ -1863,7 +1985,7 @@ namespace engine { // begin cmd buffer - cmdTransitionImageLayout(commandBuffer, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, out->image); + cmdTransitionImageLayout(commandBuffer, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, out->mipLevels, out->image); VkBufferImageCopy region{}; region.bufferOffset = 0; @@ -1880,7 +2002,8 @@ namespace engine { vkCmdCopyBufferToImage(commandBuffer, stagingBuffer, out->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); - cmdTransitionImageLayout(commandBuffer, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, out->image); + // Mipmap generation handles the transition to SHADER_READ_ONLY_OPTIMAL + cmdGenerateMipmaps(commandBuffer, out->image, w, h, out->mipLevels); // end cmd buffer endOneTimeCommands(pimpl->device, pimpl->commandPool, commandBuffer, pimpl->gfxQueue.handle); @@ -1890,11 +2013,101 @@ namespace engine { // destroy staging buffer vmaDestroyBuffer(pimpl->allocator, stagingBuffer, stagingAllocation); + // create image view + VkImageViewCreateInfo imageViewInfo{ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; + imageViewInfo.image = out->image; + imageViewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + imageViewInfo.format = VK_FORMAT_R8G8B8A8_SRGB; + imageViewInfo.subresourceRange = { + .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, + .baseMipLevel = 0, + .levelCount = out->mipLevels, + .baseArrayLayer = 0, + .layerCount = 1 + }; + + res = vkCreateImageView(pimpl->device, &imageViewInfo, nullptr, &out->imageView); + assert(res == VK_SUCCESS); + + // create texture sampler + { + VkSamplerCreateInfo samplerInfo{ VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; + samplerInfo.magFilter = VK_FILTER_LINEAR; + samplerInfo.minFilter = VK_FILTER_LINEAR; + samplerInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerInfo.anisotropyEnable = VK_TRUE; + // Find max anisotropic filtering level + { + VkPhysicalDeviceProperties properties{}; + vkGetPhysicalDeviceProperties(pimpl->physicalDevice, &properties); + + INFO("Anisotropic filtering level: {}", properties.limits.maxSamplerAnisotropy); + samplerInfo.maxAnisotropy = properties.limits.maxSamplerAnisotropy; + } + samplerInfo.borderColor = VK_BORDER_COLOR_INT_OPAQUE_BLACK; + samplerInfo.unnormalizedCoordinates = VK_FALSE; + samplerInfo.compareEnable = VK_FALSE; + samplerInfo.compareOp = VK_COMPARE_OP_ALWAYS; + samplerInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + samplerInfo.minLod = 0.0f; + samplerInfo.maxLod = static_cast(out->mipLevels); + samplerInfo.mipLodBias = 0.0f; + + res = vkCreateSampler(pimpl->device, &samplerInfo, nullptr, &out->sampler); + assert(res == VK_SUCCESS); + } + + // create descriptor pools + VkDescriptorPoolSize poolSize{}; + poolSize.type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + poolSize.descriptorCount = FRAMES_IN_FLIGHT; + + VkDescriptorPoolCreateInfo poolInfo{}; + poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; + poolInfo.poolSizeCount = 1; + poolInfo.pPoolSizes = &poolSize; + poolInfo.maxSets = FRAMES_IN_FLIGHT; + res = vkCreateDescriptorPool(pimpl->device, &poolInfo, nullptr, &out->pool); + assert(res == VK_SUCCESS); + + std::array layouts{}; + layouts.fill(pimpl->samplerSetLayout); + VkDescriptorSetAllocateInfo dSetAllocInfo{}; + dSetAllocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; + dSetAllocInfo.descriptorPool = out->pool; + dSetAllocInfo.descriptorSetCount = FRAMES_IN_FLIGHT; + dSetAllocInfo.pSetLayouts = layouts.data(); + res = vkAllocateDescriptorSets(pimpl->device, &dSetAllocInfo, out->descriptorSets.data()); + assert(res == VK_SUCCESS); + + for (int i = 0; i < FRAMES_IN_FLIGHT; i++) { + VkDescriptorImageInfo imageInfo{}; + imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + imageInfo.imageView = out->imageView; + imageInfo.sampler = out->sampler; + + VkWriteDescriptorSet descriptorWrite{}; + descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; + descriptorWrite.dstSet = out->descriptorSets[i]; + descriptorWrite.dstBinding = 0; + descriptorWrite.dstArrayElement = 0; + descriptorWrite.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + descriptorWrite.descriptorCount = 1; + descriptorWrite.pImageInfo = &imageInfo; + + vkUpdateDescriptorSets(pimpl->device, 1, &descriptorWrite, 0, nullptr); + } + return out; } void GFXDevice::destroyTexture(const gfx::Texture* texture) { + vkDestroyDescriptorPool(pimpl->device, texture->pool, nullptr); + vkDestroySampler(pimpl->device, texture->sampler, nullptr); + vkDestroyImageView(pimpl->device, texture->imageView, nullptr); vmaDestroyImage(pimpl->allocator, texture->image, texture->alloc); } diff --git a/src/resources/mesh.cpp b/src/resources/mesh.cpp index f50970d..a8622e7 100644 --- a/src/resources/mesh.cpp +++ b/src/resources/mesh.cpp @@ -31,7 +31,7 @@ static void loadMeshFromFile(const std::filesystem::path& path, std::vectorresize(header.vertex_count); fread(indices->data(), sizeof(uint32_t) * header.index_count, 1, fp); - fread(vertices->data(), sizeof(float) * 8 * header.vertex_count, 1, fp); + fread(vertices->data(), sizeof(Vertex) * header.vertex_count, 1, fp); fclose(fp); } diff --git a/src/resources/texture.cpp b/src/resources/texture.cpp index 2fdf635..ac99a1c 100644 --- a/src/resources/texture.cpp +++ b/src/resources/texture.cpp @@ -10,7 +10,7 @@ namespace engine::resources { // returns false if unable to open file -static bool readPNG(const std::string& path, std::vector& texbuf, int *width, int *height, bool *isRGBA) +static bool readPNG(const std::string& path, std::vector* texbuf, int *width, int *height, bool *isRGBA) { int x, y, n; unsigned char *data = stbi_load(path.c_str(), &x, &y, &n, STBI_rgb_alpha); @@ -22,8 +22,8 @@ static bool readPNG(const std::string& path, std::vector& texbuf, int * const size_t size = (size_t)x * (size_t)y * 4; - texbuf.resize(size); - memcpy(texbuf.data(), data, size); + texbuf->resize(size); + memcpy(texbuf->data(), data, size); *width = x; *height = y; @@ -35,7 +35,7 @@ static bool readPNG(const std::string& path, std::vector& texbuf, int * } -static bool readGLRaw(const std::string& path, std::vector& texbuf, int *width, int *height, bool *isRGBA) +static bool readGLRaw(const std::string& path, std::vector* texbuf, int *width, int *height, bool *isRGBA) { FILE *fp = fopen(path.c_str(), "rb"); if (!fp) { @@ -49,9 +49,9 @@ static bool readGLRaw(const std::string& path, std::vector& texbuf, int fseek(fp, 0L, SEEK_END); uint64_t end = ftell(fp); - texbuf.resize(end); + texbuf->resize(end); fseek(fp, (long)tex_data_offset, SEEK_SET); - fread(texbuf.data(), 1, end, fp); + fread(texbuf->data(), 1, end, fp); fclose(fp); @@ -66,15 +66,15 @@ static bool readGLRaw(const std::string& path, std::vector& texbuf, int Texture::Texture(const std::filesystem::path& resPath) : Resource(resPath, "texture") { - std::vector texbuf; + auto texbuf = std::make_unique>(); int width, height; bool isRGBA, success; - if (resPath.extension() == ".png") { - success = readPNG(resPath.string(), texbuf, &width, &height, &isRGBA); + if (resPath.extension() == ".png" || resPath.extension() == ".jpg") { + success = readPNG(resPath.string(), texbuf.get(), &width, &height, &isRGBA); } else { - success = readGLRaw(resPath.string(), texbuf, &width, &height, &isRGBA); + success = readGLRaw(resPath.string(), texbuf.get(), &width, &height, &isRGBA); } if (!success) { @@ -82,12 +82,12 @@ Texture::Texture(const std::filesystem::path& resPath) : Resource(resPath, "text } if (isRGBA == false) { - throw std::runtime_error("Currently, only RGBA textures are supported. Size: " + std::to_string(texbuf.size())); + throw std::runtime_error("Currently, only RGBA textures are supported. Size: " + std::to_string(texbuf->size())); } - m_gpuTexture = gfxdev->createTexture(texbuf.data(), (uint32_t)width, (uint32_t)height); + m_gpuTexture = gfxdev->createTexture(texbuf->data(), (uint32_t)width, (uint32_t)height); - DEBUG("loaded texture {} width: {} height: {} size: {}", resPath.filename().string(), width, height, texbuf.size()); + DEBUG("loaded texture {} width: {} height: {} size: {}", resPath.filename().string(), width, height, texbuf->size()); } @@ -96,4 +96,9 @@ Texture::~Texture() gfxdev->destroyTexture(m_gpuTexture); } +gfx::Texture* Texture::getHandle() +{ + return m_gpuTexture; +} + } \ No newline at end of file diff --git a/src/window.cpp b/src/window.cpp index c28aab2..b455747 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -276,12 +276,19 @@ namespace engine { void Window::setFullscreen(bool fullscreen, bool exclusive) { + if (m_resizable) { + + SDL_DisplayMode mode; + SDL_GetDesktopDisplayMode(SDL_GetWindowDisplayIndex(m_handle), &mode); + SDL_SetWindowDisplayMode(m_handle, &mode); + if (SDL_SetWindowFullscreen(m_handle, fullscreen ? (exclusive ? SDL_WINDOW_FULLSCREEN : SDL_WINDOW_FULLSCREEN_DESKTOP) : 0) != 0) { throw std::runtime_error("Unable to set window to fullscreen/windowed"); } m_fullscreen = fullscreen; if (fullscreen) { + int width, height; SDL_GetWindowSize(m_handle, &width, &height); onResize(width, height); @@ -291,7 +298,7 @@ namespace engine { void Window::toggleFullscreen() { - setFullscreen(!m_fullscreen, false); + setFullscreen(!m_fullscreen, true); } bool Window::isFullscreen() const diff --git a/test/game.aps b/test/game.aps new file mode 100644 index 0000000..f557b32 Binary files /dev/null and b/test/game.aps differ diff --git a/test/res/meshes/castle_0.mesh b/test/res/meshes/castle_0.mesh new file mode 100644 index 0000000..a357a4d Binary files /dev/null and b/test/res/meshes/castle_0.mesh differ diff --git a/test/res/meshes/castle_1.mesh b/test/res/meshes/castle_1.mesh new file mode 100644 index 0000000..ba7dbe0 Binary files /dev/null and b/test/res/meshes/castle_1.mesh differ diff --git a/test/res/meshes/castle_2.mesh b/test/res/meshes/castle_2.mesh new file mode 100644 index 0000000..23a2a63 Binary files /dev/null and b/test/res/meshes/castle_2.mesh differ diff --git a/test/res/meshes/castle_3.mesh b/test/res/meshes/castle_3.mesh new file mode 100644 index 0000000..704d602 Binary files /dev/null and b/test/res/meshes/castle_3.mesh differ diff --git a/test/res/meshes/castle_4.mesh b/test/res/meshes/castle_4.mesh new file mode 100644 index 0000000..bcec46d Binary files /dev/null and b/test/res/meshes/castle_4.mesh differ diff --git a/test/res/meshes/castle_5.mesh b/test/res/meshes/castle_5.mesh new file mode 100644 index 0000000..619759a Binary files /dev/null and b/test/res/meshes/castle_5.mesh differ diff --git a/test/res/shader.frag b/test/res/shader.frag index d11a185..12bab86 100644 --- a/test/res/shader.frag +++ b/test/res/shader.frag @@ -8,7 +8,7 @@ layout(location = 4) in vec3 fragColor; layout(location = 0) out vec4 outColor; -float snoise(vec2 v); +layout(set = 1, binding = 0) uniform sampler2D texSampler; void main() { @@ -16,7 +16,7 @@ void main() { vec3 lightColor = vec3(1.0, 1.0, 1.0); vec3 ambientColor = vec3(1.0, 1.0, 1.0); float ambientStrength = 0.1; - vec3 baseColor = vec3(fragColor.x * snoise(fragUV * 10.0), mod(fragUV.x, 1.0), mod(fragUV.y, 1.0)); + vec3 baseColor = vec3(texture(texSampler, fragUV)); vec3 emission = vec3(0.0, 0.0, 0.0); // code @@ -35,37 +35,4 @@ void main() { vec3 lighting = min(diffuse + ambient + specular, 1.0); outColor = min( ( vec4(baseColor, 1.0) ) * vec4(lighting + emission, 1.0), vec4(1.0)); -} - - - -// Simplex 2D noise -// -vec3 permute(vec3 x) { return mod(((x*34.0)+1.0)*x, 289.0); } - -float snoise(vec2 v){ - const vec4 C = vec4(0.211324865405187, 0.366025403784439, - -0.577350269189626, 0.024390243902439); - vec2 i = floor(v + dot(v, C.yy) ); - vec2 x0 = v - i + dot(i, C.xx); - vec2 i1; - i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0); - vec4 x12 = x0.xyxy + C.xxzz; - x12.xy -= i1; - i = mod(i, 289.0); - vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 )) - + i.x + vec3(0.0, i1.x, 1.0 )); - vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), - dot(x12.zw,x12.zw)), 0.0); - m = m*m ; - m = m*m ; - vec3 x = 2.0 * fract(p * C.www) - 1.0; - vec3 h = abs(x) - 0.5; - vec3 ox = floor(x + 0.5); - vec3 a0 = x - ox; - m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h ); - vec3 g; - g.x = a0.x * x0.x + h.x * x0.y; - g.yz = a0.yz * x12.xz + h.yz * x12.yw; - return 130.0 * dot(m, g); } \ No newline at end of file diff --git a/test/res/shader.frag.spv b/test/res/shader.frag.spv deleted file mode 100644 index a66deba..0000000 Binary files a/test/res/shader.frag.spv and /dev/null differ diff --git a/test/res/shader.vert.spv b/test/res/shader.vert.spv deleted file mode 100644 index ae4717f..0000000 Binary files a/test/res/shader.vert.spv and /dev/null differ diff --git a/test/res/textures/door.jpg b/test/res/textures/door.jpg new file mode 100644 index 0000000..d763da1 Binary files /dev/null and b/test/res/textures/door.jpg differ diff --git a/test/res/textures/grass.jpg b/test/res/textures/grass.jpg new file mode 100644 index 0000000..fd07252 Binary files /dev/null and b/test/res/textures/grass.jpg differ diff --git a/test/res/textures/metal.jpg b/test/res/textures/metal.jpg new file mode 100644 index 0000000..afcca01 Binary files /dev/null and b/test/res/textures/metal.jpg differ diff --git a/test/res/textures/rock.jpg b/test/res/textures/rock.jpg new file mode 100644 index 0000000..3df0e52 Binary files /dev/null and b/test/res/textures/rock.jpg differ diff --git a/test/src/game.cpp b/test/src/game.cpp index 35c3e24..7bdbde2 100644 --- a/test/src/game.cpp +++ b/test/src/game.cpp @@ -56,8 +56,8 @@ void playGame() auto camCamera = cam->createComponent(); camCamera->usePerspective(70.0f); cam->createComponent(); - cam->createComponent()->m_mesh = genSphereMesh(0.2f, 20); - cam->getComponent()->setTexture("textures/cobble_stone.png"); + //cam->createComponent()->m_mesh = genSphereMesh(0.2f, 20); + //cam->getComponent()->setTexture("textures/cobble_stone.png"); auto gun = cam->createChild("gun"); gun->transform.position = glm::vec3{ 0.2f, -0.1f, -0.15f }; @@ -76,7 +76,7 @@ void playGame() auto floor = app.scene()->createChild("floor"); auto floorRenderer = floor->createComponent(); floor->transform.position = glm::vec3{ 0.0f, 0.0f, 0.0f }; - floorRenderer->setTexture("textures/stone_bricks.png"); + floorRenderer->setTexture("textures/grass.jpg"); floorRenderer->m_mesh = std::make_unique(std::vector{ { { -16.0f, 0.0f, 16.0f }, { 0.0f, 1.0f, 0.0f }, { 0.0f, GRASS_DENSITY } }, { { 16.0f, 0.0f, -16.0f }, { 0.0f, 1.0f, 0.0f }, { GRASS_DENSITY, 0.0f } }, @@ -128,5 +128,30 @@ void playGame() sphereRenderer->m_mesh = genSphereMesh(5.0f, 100, false); sphereRenderer->setTexture("textures/cobble_stone.png"); + /* castle */ + auto castle = app.scene()->createChild("castle"); + castle->transform.scale = { 0.01f, 0.01f, 0.01f }; + std::vector castleParts(6); + for (int i = 0; i < castleParts.size(); i++) { + if (i == 2) continue; + castleParts[i] = castle->createChild(std::to_string(i)); + auto ren = castleParts[i]->createComponent(); + ren->setMesh("meshes/castle_" + std::to_string(i) + ".mesh"); + ren->setTexture("textures/rock.jpg"); + + if (i == 5) { + ren->setTexture("textures/metal.jpg"); + } + if (i == 4) { + ren->setTexture("textures/door.jpg"); + } + } + + // boundary + auto bounds = app.scene()->createChild("bounds"); + auto boundsRen = bounds->createComponent(); + boundsRen->m_mesh = genSphereMesh(100.0f, 100, true); + boundsRen->setTexture("textures/metal.jpg"); + app.gameLoop(); }