diff --git a/base/VulkanglTFModel.cpp b/base/VulkanglTFModel.cpp index 8cae61ea..68f4105c 100644 --- a/base/VulkanglTFModel.cpp +++ b/base/VulkanglTFModel.cpp @@ -613,6 +613,110 @@ vkglTF::Texture* vkglTF::Model::getTexture(uint32_t index) return nullptr; } +void vkglTF::Model::createEmptyTexture(VkQueue transferQueue) +{ + emptyTexture.device = device; + emptyTexture.width = 1; + emptyTexture.height = 1; + emptyTexture.layerCount = 1; + emptyTexture.mipLevels = 1; + + size_t bufferSize = emptyTexture.width * emptyTexture.height * 4; + unsigned char* buffer = new unsigned char[bufferSize]; + memset(buffer, 0, bufferSize); + + VkBuffer stagingBuffer; + VkDeviceMemory stagingMemory; + VkBufferCreateInfo bufferCreateInfo = vks::initializers::bufferCreateInfo(); + bufferCreateInfo.size = bufferSize; + // This buffer is used as a transfer source for the buffer copy + bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + bufferCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + VK_CHECK_RESULT(vkCreateBuffer(device->logicalDevice, &bufferCreateInfo, nullptr, &stagingBuffer)); + + VkMemoryAllocateInfo memAllocInfo = vks::initializers::memoryAllocateInfo(); + VkMemoryRequirements memReqs; + vkGetBufferMemoryRequirements(device->logicalDevice, stagingBuffer, &memReqs); + memAllocInfo.allocationSize = memReqs.size; + memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + VK_CHECK_RESULT(vkAllocateMemory(device->logicalDevice, &memAllocInfo, nullptr, &stagingMemory)); + VK_CHECK_RESULT(vkBindBufferMemory(device->logicalDevice, stagingBuffer, stagingMemory, 0)); + + // Copy texture data into staging buffer + uint8_t* data; + VK_CHECK_RESULT(vkMapMemory(device->logicalDevice, stagingMemory, 0, memReqs.size, 0, (void**)&data)); + memcpy(data, buffer, bufferSize); + vkUnmapMemory(device->logicalDevice, stagingMemory); + + VkBufferImageCopy bufferCopyRegion = {}; + bufferCopyRegion.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + bufferCopyRegion.imageSubresource.layerCount = 1; + bufferCopyRegion.imageExtent.width = emptyTexture.width; + bufferCopyRegion.imageExtent.height = emptyTexture.height; + bufferCopyRegion.imageExtent.depth = 1; + + // Create optimal tiled target image + VkImageCreateInfo imageCreateInfo = vks::initializers::imageCreateInfo(); + imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; + imageCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM; + imageCreateInfo.mipLevels = 1; + imageCreateInfo.arrayLayers = 1; + imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; + imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; + imageCreateInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + imageCreateInfo.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; + imageCreateInfo.extent = { emptyTexture.width, emptyTexture.height, 1 }; + imageCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; + VK_CHECK_RESULT(vkCreateImage(device->logicalDevice, &imageCreateInfo, nullptr, &emptyTexture.image)); + + vkGetImageMemoryRequirements(device->logicalDevice, emptyTexture.image, &memReqs); + memAllocInfo.allocationSize = memReqs.size; + memAllocInfo.memoryTypeIndex = device->getMemoryType(memReqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + VK_CHECK_RESULT(vkAllocateMemory(device->logicalDevice, &memAllocInfo, nullptr, &emptyTexture.deviceMemory)); + VK_CHECK_RESULT(vkBindImageMemory(device->logicalDevice, emptyTexture.image, emptyTexture.deviceMemory, 0)); + + VkImageSubresourceRange subresourceRange{}; + subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + subresourceRange.baseMipLevel = 0; + subresourceRange.levelCount = 1; + subresourceRange.layerCount = 1; + + VkCommandBuffer copyCmd = device->createCommandBuffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true); + vks::tools::setImageLayout(copyCmd, emptyTexture.image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, subresourceRange); + vkCmdCopyBufferToImage(copyCmd, stagingBuffer, emptyTexture.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &bufferCopyRegion); + vks::tools::setImageLayout(copyCmd, emptyTexture.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, subresourceRange); + device->flushCommandBuffer(copyCmd, transferQueue); + emptyTexture.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + // Clean up staging resources + vkFreeMemory(device->logicalDevice, stagingMemory, nullptr); + vkDestroyBuffer(device->logicalDevice, stagingBuffer, nullptr); + + VkSamplerCreateInfo samplerCreateInfo = vks::initializers::samplerCreateInfo(); + samplerCreateInfo.magFilter = VK_FILTER_LINEAR; + samplerCreateInfo.minFilter = VK_FILTER_LINEAR; + samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; + samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerCreateInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerCreateInfo.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; + samplerCreateInfo.compareOp = VK_COMPARE_OP_NEVER; + samplerCreateInfo.maxAnisotropy = 1.0f; + VK_CHECK_RESULT(vkCreateSampler(device->logicalDevice, &samplerCreateInfo, nullptr, &emptyTexture.sampler)); + + VkImageViewCreateInfo viewCreateInfo = vks::initializers::imageViewCreateInfo(); + viewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; + viewCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM; + viewCreateInfo.components = { VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G, VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A }; + viewCreateInfo.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; + viewCreateInfo.subresourceRange.levelCount = 1; + viewCreateInfo.image = emptyTexture.image; + VK_CHECK_RESULT(vkCreateImageView(device->logicalDevice, &viewCreateInfo, nullptr, &emptyTexture.view)); + + emptyTexture.descriptor.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + emptyTexture.descriptor.imageView = emptyTexture.view; + emptyTexture.descriptor.sampler = emptyTexture.sampler; +} + /* glTF model loading and rendering class */ @@ -637,6 +741,7 @@ vkglTF::Model::~Model() descriptorSetLayoutImage = VK_NULL_HANDLE; } vkDestroyDescriptorPool(device->logicalDevice, descriptorPool, nullptr); + emptyTexture.destroy(); } void vkglTF::Model::loadNode(vkglTF::Node *parent, const tinygltf::Node &node, uint32_t nodeIndex, const tinygltf::Model &model, std::vector& indexBuffer, std::vector& vertexBuffer, float globalscale) @@ -876,6 +981,8 @@ void vkglTF::Model::loadImages(tinygltf::Model &gltfModel, vks::VulkanDevice *de texture.fromglTfImage(image, path, device, transferQueue); textures.push_back(texture); } + // Create an empty texture to be used for empty material images + createEmptyTexture(transferQueue); } void vkglTF::Model::loadMaterials(tinygltf::Model &gltfModel) @@ -900,6 +1007,8 @@ void vkglTF::Model::loadMaterials(tinygltf::Model &gltfModel) } if (mat.additionalValues.find("normalTexture") != mat.additionalValues.end()) { material.normalTexture = getTexture(gltfModel.textures[mat.additionalValues["normalTexture"].TextureIndex()].source); + } else { + material.normalTexture = &emptyTexture; } if (mat.additionalValues.find("emissiveTexture") != mat.additionalValues.end()) { material.emissiveTexture = getTexture(gltfModel.textures[mat.additionalValues["emissiveTexture"].TextureIndex()].source); @@ -924,7 +1033,6 @@ void vkglTF::Model::loadMaterials(tinygltf::Model &gltfModel) } // Push a default material at the end of the list for meshes with no material assigned materials.push_back(Material(device)); - } void vkglTF::Model::loadAnimations(tinygltf::Model &gltfModel) diff --git a/base/VulkanglTFModel.h b/base/VulkanglTFModel.h index 3a1fd984..21d936fc 100644 --- a/base/VulkanglTFModel.h +++ b/base/VulkanglTFModel.h @@ -249,6 +249,8 @@ namespace vkglTF class Model { private: vkglTF::Texture* getTexture(uint32_t index); + vkglTF::Texture emptyTexture; + void createEmptyTexture(VkQueue transferQueue); public: vks::VulkanDevice* device; VkDescriptorPool descriptorPool;